From 327ad42ff2570b5495bacdc0d5be9c778ed6f8b7 Mon Sep 17 00:00:00 2001 From: Ivan Bojer Date: Wed, 1 Feb 2017 04:41:52 -0800 Subject: [PATCH] new panos module to import software or configuration file into firewall (#19901) * # This is a combination of 6 commits. # The first commit's message is: new module to import software or configuration file onto firewall # This is the 2nd commit message: changes based on the review comments; remove unecessary if statements; change returned value docstring # This is the 3rd commit message: empty checkin to trigger ANSIBLEbot # This is the 4th commit message: added additional exception handling # This is the 5th commit message: - added new module info to the changelog as requested # This is the 6th commit message: removed blank space as tox checks were failing * new module to import software or configuration file onto firewall --- CHANGELOG.md | 2 + .../modules/network/panos/panos_import.py | 196 ++++++++++++++++++ 2 files changed, 198 insertions(+) create mode 100755 lib/ansible/modules/network/panos/panos_import.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 5791e7f5ec..8f1b4ba007 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -130,6 +130,8 @@ Ansible Changes By Release * packet_device * packet_sshkey - runit +- panos: + * panos_import - smartos: * imgadm * vmadm diff --git a/lib/ansible/modules/network/panos/panos_import.py b/lib/ansible/modules/network/panos/panos_import.py new file mode 100755 index 0000000000..45de787768 --- /dev/null +++ b/lib/ansible/modules/network/panos/panos_import.py @@ -0,0 +1,196 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Ansible module to manage PaloAltoNetworks Firewall +# (c) 2016, techbizdev +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +DOCUMENTATION = ''' +--- +module: panos_import +short_description: import file on PAN-OS devices +description: + - Import file on PAN-OS device +author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)" +version_added: "2.3" +requirements: + - pan-python + - requests + - requests_toolbelt +options: + ip_address: + description: + - IP address (or hostname) of PAN-OS device. + required: true + password: + description: + - Password for device authentication. + required: true + username: + description: + - Username for device authentication. + required: false + default: "admin" + 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 +''' + +EXAMPLES = ''' +# import software image PanOS_vm-6.1.1 on 192.168.1.1 +- name: import software image into PAN-OS + panos_import: + ip_address: 192.168.1.1 + username: admin + password: admin + file: /tmp/PanOS_vm-6.1.1 + category: software +''' + +RETURN=''' +# Default return values +''' + +ANSIBLE_METADATA = {'status': ['preview'], + 'supported_by': 'community', + 'version': '1.0'} + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.basic import get_exception + +import os.path +import xml.etree +import tempfile +import shutil +import os + +try: + import pan.xapi + import requests + import requests_toolbelt + HAS_LIB = True +except ImportError: + HAS_LIB = False + + +def import_file(xapi, module, ip_address, file_, category): + xapi.keygen() + + params = { + 'type': 'import', + 'category': category, + 'key': xapi.api_key + } + + 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(): + argument_spec = dict( + ip_address=dict(required=True), + password=dict(required=True, no_log=True), + username=dict(default='admin'), + category=dict(default='software'), + file=dict(), + url=dict() + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, required_one_of=[['file', 'url']]) + if not HAS_LIB: + module.fail_json(msg='pan-python, requests, and requests_toolbelt are required for this module') + + ip_address = module.params["ip_address"] + password = module.params["password"] + username = module.params['username'] + + xapi = pan.xapi.PanXapi( + hostname=ip_address, + api_username=username, + api_password=password + ) + + file_ = module.params['file'] + url = module.params['url'] + + category = module.params['category'] + + # we can get file from URL or local storage + if url is not None: + file_ = download_file(url) + + try: + changed, filename = import_file(xapi, module, ip_address, file_, category) + 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()