From 26a991c51fac3311467dc27b0008f3b13bad08f0 Mon Sep 17 00:00:00 2001 From: Garfield Lee Freeman Date: Mon, 13 Sep 2021 13:03:51 -0700 Subject: [PATCH] feat: Add device group support (#250) * feat: Add device group support fixes #102 --- Makefile | 1 + plugins/modules/panos_device_group.py | 178 ++++++++++++++++++++++++++ requirements.txt | 2 +- tests/sanity/ignore-2.10.txt | 1 + tests/sanity/ignore-2.11.txt | 1 + tests/sanity/ignore-2.9.txt | 3 +- 6 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 plugins/modules/panos_device_group.py diff --git a/Makefile b/Makefile index 771952ae7..8b13ceb1f 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,7 @@ format: ## Format with black, isort check-format: ## Check with black, isort black --check . + isort --diff . isort --check . sync-deps: ## Sync Pipfile.lock to requirements.txt diff --git a/plugins/modules/panos_device_group.py b/plugins/modules/panos_device_group.py new file mode 100644 index 000000000..373a8a573 --- /dev/null +++ b/plugins/modules/panos_device_group.py @@ -0,0 +1,178 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright 2021 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. + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +DOCUMENTATION = """ +--- +module: panos_device_group +short_description: configure Panorama device group +description: + - Configure Panorama device group. +author: + - Garfield Lee Freeman (@shinmog) +version_added: '2.8.0' +requirements: + - pan-python can be obtained from PyPI U(https://pypi.python.org/pypi/pan-python) + - pandevice can be obtained from PyPI U(https://pypi.python.org/pypi/pandevice) + - pandevice >= 1.5.1 +notes: + - Panorama is supported. + - This is a Panorama only module. + - Check mode is supported. +extends_documentation_fragment: + - paloaltonetworks.panos.fragments.state + - paloaltonetworks.panos.fragments.provider +options: + name: + description: + - Name of the device group. + type: str + required: true + tag: + description: + - List of tags + type: list + elements: str + parent: + description: + - Name of the device group parent. + - An empty parent means the parent device group should be "shared". + type: str +""" + +EXAMPLES = """ +# Create a device group under shared. +- name: Create device group + panos_device_group: + provider: '{{ provider }}' + name: 'hello world' + +# Create a device group under "hello world" +- name: Create device group under hello world + panos_device_group: + provider: '{{ provider }}' + name: 'child' + parent: 'hello world' + +# Delete the child device group +- name: Delete a device group. + panos_device_group: + provider: '{{ provider }}' + name: 'some device group' + state: 'absent' +""" + +RETURN = """ +# Default return values +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.paloaltonetworks.panos.plugins.module_utils.panos import ( + get_connection, +) + +try: + from panos.errors import PanDeviceError, PanObjectMissing + from panos.panorama import DeviceGroup +except ImportError: + try: + from pandevice.errors import PanDeviceError, PanObjectMissing + from pandevice.panorama import DeviceGroup + except ImportError: + pass + + +def main(): + helper = get_connection( + with_state=True, + firewall_error="This is a Panorama only module", + min_pandevice_version=(1, 5, 1), + argument_spec=dict( + name=dict(required=True), + tag=dict(type="list", elements="str"), + parent=dict(), + ), + ) + module = AnsibleModule( + argument_spec=helper.argument_spec, + supports_check_mode=True, + required_one_of=helper.required_one_of, + ) + + # Verify imports, build pandevice object tree. + parent = helper.get_pandevice_parent(module) + + # Object params. + spec = { + "name": module.params["name"], + "tag": module.params["tag"], + } + + # Check for current object. + listing = [] + live_obj = DeviceGroup(spec["name"]) + parent.add(live_obj) + try: + live_obj.refresh() + except PanObjectMissing: + pass + except PanDeviceError as e: + module.fail_json(msg="Failed refresh: {0}".format(e)) + else: + listing.append(live_obj) + + # Build the object and attach to the parent. + obj = DeviceGroup(**spec) + parent.add(obj) + + # Opstate: get the current device group parent. + try: + obj.opstate.dg_hierarchy.refresh() + except PanDeviceError as e: + module.fail_json(msg="Failed dg hierarchy refresh: {0}".format(e)) + + # Perform the requeseted action. + changed, diff = helper.apply_state(obj, listing, module) + + # TODO(shinmog): perhaps this should be moved into the helper..? + if diff is None: + diff = {} + + # Opstate handling: device group parent. + diff["before_parent"] = obj.opstate.dg_hierarchy.parent + parent = module.params["parent"] or None + if module.params["state"] == "absent": + diff["after_parent"] = None + elif obj.opstate.dg_hierarchy.parent != parent: + diff["after_parent"] = parent + obj.opstate.dg_hierarchy.parent = parent + changed = True + if not module.check_mode: + try: + obj.opstate.dg_hierarchy.update() + except PanDeviceError as e: + module.fail_json(msg="Failed to set dg parent: {0}".format(e)) + + # Done! + module.exit_json(changed=changed, diff=diff, msg="Done!") + + +if __name__ == "__main__": + main() diff --git a/requirements.txt b/requirements.txt index 1ac9f175d..8f4afe183 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ certifi==2021.5.30 chardet==3.0.4 idna==2.8 -pan-os-python==1.2.0 +pan-os-python==1.5.1 pan-python==0.16.0 requests==2.22.0 urllib3==1.25.11; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4' diff --git a/tests/sanity/ignore-2.10.txt b/tests/sanity/ignore-2.10.txt index 3afb0e312..af33c6aa5 100644 --- a/tests/sanity/ignore-2.10.txt +++ b/tests/sanity/ignore-2.10.txt @@ -38,6 +38,7 @@ plugins/modules/panos_dag_tags.py no-get-exception plugins/modules/panos_dag_tags.py validate-modules:deprecation-mismatch plugins/modules/panos_dag_tags.py validate-modules:invalid-documentation plugins/modules/panos_dag_tags.py validate-modules:missing-gplv3-license +plugins/modules/panos_device_group.py validate-modules:missing-gplv3-license plugins/modules/panos_dynamic_updates.py validate-modules:missing-gplv3-license plugins/modules/panos_dynamic_user_group.py validate-modules:missing-gplv3-license plugins/modules/panos_email_profile.py validate-modules:missing-gplv3-license diff --git a/tests/sanity/ignore-2.11.txt b/tests/sanity/ignore-2.11.txt index 3afb0e312..af33c6aa5 100644 --- a/tests/sanity/ignore-2.11.txt +++ b/tests/sanity/ignore-2.11.txt @@ -38,6 +38,7 @@ plugins/modules/panos_dag_tags.py no-get-exception plugins/modules/panos_dag_tags.py validate-modules:deprecation-mismatch plugins/modules/panos_dag_tags.py validate-modules:invalid-documentation plugins/modules/panos_dag_tags.py validate-modules:missing-gplv3-license +plugins/modules/panos_device_group.py validate-modules:missing-gplv3-license plugins/modules/panos_dynamic_updates.py validate-modules:missing-gplv3-license plugins/modules/panos_dynamic_user_group.py validate-modules:missing-gplv3-license plugins/modules/panos_email_profile.py validate-modules:missing-gplv3-license diff --git a/tests/sanity/ignore-2.9.txt b/tests/sanity/ignore-2.9.txt index 6e603a560..af33c6aa5 100644 --- a/tests/sanity/ignore-2.9.txt +++ b/tests/sanity/ignore-2.9.txt @@ -38,6 +38,7 @@ plugins/modules/panos_dag_tags.py no-get-exception plugins/modules/panos_dag_tags.py validate-modules:deprecation-mismatch plugins/modules/panos_dag_tags.py validate-modules:invalid-documentation plugins/modules/panos_dag_tags.py validate-modules:missing-gplv3-license +plugins/modules/panos_device_group.py validate-modules:missing-gplv3-license plugins/modules/panos_dynamic_updates.py validate-modules:missing-gplv3-license plugins/modules/panos_dynamic_user_group.py validate-modules:missing-gplv3-license plugins/modules/panos_email_profile.py validate-modules:missing-gplv3-license @@ -80,8 +81,8 @@ plugins/modules/panos_pg.py validate-modules:missing-gplv3-license plugins/modules/panos_query_rules.py validate-modules:deprecation-mismatch plugins/modules/panos_query_rules.py validate-modules:invalid-documentation plugins/modules/panos_query_rules.py validate-modules:missing-gplv3-license -plugins/modules/panos_region.py validate-modules:missing-gplv3-license plugins/modules/panos_redistribution.py validate-modules:missing-gplv3-license +plugins/modules/panos_region.py validate-modules:missing-gplv3-license plugins/modules/panos_registered_ip.py validate-modules:missing-gplv3-license plugins/modules/panos_registered_ip_facts.py validate-modules:missing-gplv3-license plugins/modules/panos_restart.py validate-modules:missing-gplv3-license