From b2b6fa55054a17ecace4b64d9edea4efb2a52ecf Mon Sep 17 00:00:00 2001 From: Paulo Luna Date: Tue, 31 Mar 2020 14:41:14 -0300 Subject: [PATCH] added the panos_import_file module added the panos_import_file module based on the panos_import module with added login via provider and a dictionary of extra arguments that may be required for specific imports --- library/panos_import_file.py | 197 +++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 library/panos_import_file.py diff --git a/library/panos_import_file.py b/library/panos_import_file.py new file mode 100644 index 0000000..0204419 --- /dev/null +++ b/library/panos_import_file.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright 2016 Palo Alto Networks, Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import os.path +import shutil +import tempfile +import xml.etree + +from ansible.module_utils.basic import AnsibleModule, get_exception +from ansible.module_utils.network.panos.panos import get_connection + +DOCUMENTATION = ''' +--- +module: panos_import_file +short_description: import file on PAN-OS devices +description: + - Import file on PAN-OS device +author: "Paulo Luna (@pauloluna), based on panos_import from Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)" +version_added: "1.0" +requirements: + - pan-python + - requests + - requests_toolbelt +options: + category: + description: + - Category of file uploaded. The default is software. + required: false + default: software + file: + description: + - Location of the file to import into device. + required: false + default: None + url: + description: + - URL of the file that will be imported to device. + required: false + default: None + extra_args: + description: + - A dictionary of extra arguments that may be required for specific imports. +''' + +EXAMPLES = ''' +# import software image PanOS_vm-6.1.1 on 192.168.1.1 +- name: import software image into PAN-OS + panos_import_file: + provider: '{{ provider }}' + file: /tmp/PanOS_vm-6.1.1 + category: software + +# import a certificate +- name: import certificate + panos_import_file: + provider: '{{ provider }}' + file: /tmp/MyCertificate.crt + category: certificate + extra_args: + format: pem + certificate-name: MyCertificate + passphrase: '{{ certificate_password }}' +''' + +RETURN = ''' +# Default return values +''' + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +try: + import requests + import requests_toolbelt + HAS_LIB = True +except ImportError: + HAS_LIB = False + + +def import_file(xapi, module, ip_address, file_, category, extra_args): + + params = { + 'type': 'import', + 'category': category, + 'key': xapi.api_key + } + + for key, value in extra_args.items(): + params[key] = str(value) + + filename = os.path.basename(file_) + + mef = requests_toolbelt.MultipartEncoder( + fields={ + 'file': (filename, open(file_, 'rb'), 'application/octet-stream') + } + ) + + r = requests.post( + 'https://' + ip_address + '/api/', + verify=False, + params=params, + headers={'Content-Type': mef.content_type}, + data=mef + ) + + # if something goes wrong just raise an exception + r.raise_for_status() + + resp = xml.etree.ElementTree.fromstring(r.content) + + if resp.attrib['status'] == 'error': + module.fail_json(msg=r.content) + + return True, filename + + +def download_file(url): + r = requests.get(url, stream=True) + fo = tempfile.NamedTemporaryFile(prefix='ai', delete=False) + shutil.copyfileobj(r.raw, fo) + fo.close() + + return fo.name + + +def delete_file(path): + os.remove(path) + + +def main(): + helper = get_connection( + with_classic_provider_spec=True, + argument_spec=dict( + category=dict(default='software'), + file=dict(), + url=dict(), + extra_args=dict(type='dict', default=dict()) + ), + required_one_of=[['file', 'url']] + ) + + module = AnsibleModule( + argument_spec=helper.argument_spec, + supports_check_mode=False, + required_one_of=helper.required_one_of, + ) + parent = helper.get_pandevice_parent(module) + + if not HAS_LIB: + module.fail_json( + msg='pan-python, requests, and requests_toolbelt are required for this module') + + file_ = module.params['file'] + url = module.params['url'] + + category = module.params['category'] + + extra_args = module.params['extra_args'] + + # we can get file from URL or local storage + if url is not None: + file_ = download_file(url) + + try: + changed, filename = import_file( + parent.xapi, module, parent.hostname, file_, category, extra_args) + except Exception: + exc = get_exception() + module.fail_json(msg=exc.message) + + # cleanup and delete file if local + if url is not None: + delete_file(file_) + + module.exit_json(changed=changed, filename=filename, msg="okey dokey") + + +if __name__ == '__main__': + main()