From c918a6eda535d4f059a2540de9bce36f108793e1 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Fri, 12 Jan 2024 12:49:36 -0500 Subject: [PATCH 01/15] [minor_change] Add aci_netflow_monitor_policy and aci_netflow_record_policy as new modules. --- plugins/module_utils/constants.py | 29 ++ plugins/modules/aci_netflow_monitor_policy.py | 295 +++++++++++++++++ plugins/modules/aci_netflow_record_policy.py | 302 ++++++++++++++++++ .../aci_netflow_monitor_policy/aliases | 2 + .../aci_netflow_monitor_policy/tasks/main.yml | 150 +++++++++ .../targets/aci_netflow_record_policy/aliases | 2 + .../aci_netflow_record_policy/tasks/main.yml | 178 +++++++++++ 7 files changed, 958 insertions(+) create mode 100644 plugins/modules/aci_netflow_monitor_policy.py create mode 100644 plugins/modules/aci_netflow_record_policy.py create mode 100644 tests/integration/targets/aci_netflow_monitor_policy/aliases create mode 100644 tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml create mode 100644 tests/integration/targets/aci_netflow_record_policy/aliases create mode 100644 tests/integration/targets/aci_netflow_record_policy/tasks/main.yml diff --git a/plugins/module_utils/constants.py b/plugins/module_utils/constants.py index 55fe23fff..aa5f9ad2b 100644 --- a/plugins/module_utils/constants.py +++ b/plugins/module_utils/constants.py @@ -192,3 +192,32 @@ MATCH_PIM_INTERFACE_POLICY_CONTROL_STATE_MAPPING = dict(multicast_domain_boundary="border", strict_rfc_compliant="strict-rfc-compliant", passive="passive") MATCH_PIM_INTERFACE_POLICY_AUTHENTICATION_TYPE_MAPPING = dict(none="none", md5_hmac="ah-md5") + +MATCH_COLLECT_NETFLOW_RECORD_MAPPING = dict( + bytes_counter="count-bytes", + pkts_counter="count-pkts", + pkt_disposition="pkt-disp", + sampler_id="sampler-id", + source_interface="src-intf", + tcp_flags="tcp-flags", + first_pkt_timestamp="ts-first", + recent_pkt_timestamp="ts-recent", +) + +MATCH_MATCH_NETFLOW_RECORD_MAPPING = dict( + destination_ipv4_v6="dst-ip", + destination_ipv4="dst-ipv4", + destination_ipv6="dst-ipv6", + destination_mac="dst-mac", + destination_port="dst-port", + ethertype="ethertype", + ip_protocol="proto", + source_ipv4_v6="src-ip", + source_ipv4="src-ipv4", + source_ipv6="src-ipv6", + source_mac="src-mac", + source_port="src-port", + ip_tos="tos", + unspecified="unspecified", + vlan="vlan", +) diff --git a/plugins/modules/aci_netflow_monitor_policy.py b/plugins/modules/aci_netflow_monitor_policy.py new file mode 100644 index 000000000..75f2ce362 --- /dev/null +++ b/plugins/modules/aci_netflow_monitor_policy.py @@ -0,0 +1,295 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2023, Gaspard Micol (@gmicol) +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"} + +DOCUMENTATION = r""" +--- +module: aci_netflow_monitor_policy +short_description: Manage Netflow Monitor Policy (netflow:MonitorPol) +description: +- Manage Netflow Monitor Policies for tenants on Cisco ACI fabrics. +options: + tenant: + description: + - The name of an existing tenant. + type: str + aliases: [ tenant_name ] + netflow_monitor_policy: + description: + - The name of the Netflow Monitor Policy. + type: str + aliases: [ netflow_monitor, netflow_monitor_name, name ] + netflow_record_policy: + description: + - The name of the Netflow Record Policy. + type: str + aliases: [ netflow_record, netflow_record_name ] + description: + description: + - The description for the Netflow Monitor Policy. + type: str + aliases: [ descr ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci +- cisco.aci.annotation +- cisco.aci.owner + +notes: +- The I(tenant) must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(netflow:MonitorPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Gaspard Micol (@gmicol) +""" + +EXAMPLES = r""" +- name: Add a new Netflow Monitor Policy + cisco.aci.aci_netflow_monitor_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + netflow_monitor_policy: my_netflow_monitor_policy + state: present + delegate_to: localhost + +- name: Query a Netflow Monitor Policy + cisco.aci.aci_netflow_monitor_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + netflow_monitor_policy: my_netflow_monitor_policy + state: query + delegate_to: localhost + +- name: Query all Netflow Monitor Policies in my_tenant + cisco.aci.aci_netflow_monitor_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + state: query + delegate_to: localhost + +- name: Query all Netflow Monitor Policies + cisco.aci.aci_netflow_monitor_policy: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + +- name: Delete a Netflow Monitor Policy + cisco.aci.aci_netflow_monitor_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + netflow_monitor_policy: my_netflow_monitor_policy + state: absent + delegate_to: localhost +""" + +RETURN = r""" +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update(aci_annotation_spec()) + argument_spec.update(aci_owner_spec()) + argument_spec.update( + tenant=dict(type="str", aliases=["tenant_name"]), + netflow_monitor_policy=dict(type="str", aliases=["netflow_monitor", "netflow_monitor_name", "name"]), + netflow_record_policy=dict(type="str", aliases=["netflow_record", "netflow_record_name"]), + description=dict(type="str", aliases=["descr"]), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "absent", ["tenant", "netflow_monitor_policy"]], + ["state", "present", ["tenant", "netflow_monitor_policy"]], + ], + ) + + tenant = module.params.get("tenant") + description = module.params.get("description") + netflow_monitor_policy = module.params.get("netflow_monitor_policy") + netflow_record_policy = module.params.get("netflow_record_policy") + state = module.params.get("state") + + aci = ACIModule(module) + + child_classes = ["netflowRsMonitorToRecord"] + + aci.construct_url( + root_class=dict( + aci_class="fvTenant", + aci_rn="tn-{0}".format(tenant), + module_object=tenant, + target_filter={"name": tenant}, + ), + subclass_1=dict( + aci_class="netflowMonitorPol", + aci_rn="monitorpol-{0}".format(netflow_monitor_policy), + module_object=netflow_monitor_policy, + target_filter={"name": netflow_monitor_policy}, + ), + child_classes=child_classes, + ) + + aci.get_existing() + + if state == "present": + child_configs = [] + child_configs.append(dict(netflowRsMonitorToRecord=dict(attributes=dict(tnNetflowRecordPolName=netflow_record_policy)))) + aci.payload( + aci_class="netflowMonitorPol", + class_config=dict( + name=netflow_monitor_policy, + descr=description, + ), + child_configs=child_configs, + ) + + aci.get_diff(aci_class="netflowMonitorPol") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/aci_netflow_record_policy.py b/plugins/modules/aci_netflow_record_policy.py new file mode 100644 index 000000000..6e6f5f96f --- /dev/null +++ b/plugins/modules/aci_netflow_record_policy.py @@ -0,0 +1,302 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2023, Gaspard Micol (@gmicol) +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"} + +DOCUMENTATION = r""" +--- +module: aci_netflow_record_policy +short_description: Manage Netflow Record Policy (netflow:RecordPol) +description: +- Manage Netflow Record Policies for tenants on Cisco ACI fabrics. +options: + tenant: + description: + - The name of an existing tenant. + type: str + aliases: [ tenant_name ] + netflow_record_policy: + description: + - The name of the Netflow Record Policy. + type: str + aliases: [ netflow_record, netflow_record_name, name ] + collect: + description: + - The collect parameters for the flow record. + - The APIC defaults to C(source_interface) when unset during creation. + type: list + elements: str + choices: [ bytes_counter, pkts_counter, pkt_disposition, sampler_id, source_interface, tcp_flags, first_pkt_timestamp, recent_pkt_timestamp ] + match: + description: + - The match parameters for the flow record. + type: list + elements: str + choices: [ destination_ipv4_v6, destination_ipv4, destination_ipv6, destination_mac, destination_port, ethertype, ip_protocol, source_ipv4_v6, source_ipv4, source_ipv6, source_mac, source_port, ip_tos, unspecified, vlan ] + description: + description: + - The description for the Netflow Record Policy. + type: str + aliases: [ descr ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci +- cisco.aci.annotation +- cisco.aci.owner + +notes: +- The I(tenant) must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(netflow:RecordPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Gaspard Micol (@gmicol) +""" + +EXAMPLES = r""" +- name: Add a new Netflow Record Policy + cisco.aci.aci_netflow_record_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + netflow_record_policy: my_netflow_record_policy + state: present + delegate_to: localhost + +- name: Query a Netflow Record Policy + cisco.aci.aci_netflow_record_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + netflow_record_policy: my_netflow_record_policy + state: query + delegate_to: localhost + +- name: Query all Netflow Record Policies in my_tenant + cisco.aci.aci_netflow_record_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + state: query + delegate_to: localhost + +- name: Query all Netflow Record Policies + cisco.aci.aci_netflow_record_policy: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + +- name: Delete a Netflow Record Policy + cisco.aci.aci_netflow_record_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + netflow_record_policy: my_netflow_record_policy + state: absent + delegate_to: localhost +""" + +RETURN = r""" +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec +from ansible_collections.cisco.aci.plugins.module_utils.constants import MATCH_COLLECT_NETFLOW_RECORD_MAPPING, MATCH_MATCH_NETFLOW_RECORD_MAPPING + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update(aci_annotation_spec()) + argument_spec.update(aci_owner_spec()) + argument_spec.update( + tenant=dict(type="str", aliases=["tenant_name"]), + netflow_record_policy=dict(type="str", aliases=["netflow_record", "netflow_record_name", "name"]), + collect=dict(type="list", elements="str", choices=list(MATCH_COLLECT_NETFLOW_RECORD_MAPPING.keys())), + match=dict(type="list", elements="str", choices=list(MATCH_MATCH_NETFLOW_RECORD_MAPPING.keys())), + description=dict(type="str", aliases=["descr"]), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "absent", ["tenant", "netflow_record_policy"]], + ["state", "present", ["tenant", "netflow_record_policy"]], + ], + ) + + tenant = module.params.get("tenant") + description = module.params.get("description") + netflow_record_policy = module.params.get("netflow_record_policy") + state = module.params.get("state") + collect = ",".join(sorted(MATCH_COLLECT_NETFLOW_RECORD_MAPPING.get(v) for v in module.params.get("collect"))) if module.params.get("collect") is not None else None + match = ",".join(sorted(MATCH_MATCH_NETFLOW_RECORD_MAPPING.get(v) for v in module.params.get("match"))) if module.params.get("match") is not None else None + + aci = ACIModule(module) + + aci.construct_url( + root_class=dict( + aci_class="fvTenant", + aci_rn="tn-{0}".format(tenant), + module_object=tenant, + target_filter={"name": tenant}, + ), + subclass_1=dict( + aci_class="netflowRecordPol", + aci_rn="recordpol-{0}".format(netflow_record_policy), + module_object=netflow_record_policy, + target_filter={"name": netflow_record_policy}, + ), + ) + + aci.get_existing() + + if state == "present": + aci.payload( + aci_class="netflowRecordPol", + class_config=dict( + name=netflow_record_policy, + collect=collect, + match=match, + descr=description, + ), + ) + + aci.get_diff(aci_class="netflowRecordPol") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/aci_netflow_monitor_policy/aliases b/tests/integration/targets/aci_netflow_monitor_policy/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_netflow_monitor_policy/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml b/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml new file mode 100644 index 000000000..7149221f3 --- /dev/null +++ b/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml @@ -0,0 +1,150 @@ +# Test code for the ACI modules +# Copyright: (c) 2023, Gaspard Micol (@gmicol) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + ansible.builtin.fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + ansible.builtin.set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +# CLEAN ENVIRONMENT BEFORE TESTS +- name: Remove the ansible_tenant + cisco.aci.aci_tenant: &aci_tenant_absent + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Verify Cloud and Non-Cloud Sites in use. + ansible.builtin.include_tasks: ../../../../../../integration/targets/aci_cloud_provider/tasks/main.yml + +- name: Execute tasks only for non-cloud sites + when: query_cloud.current == [] # This condition will execute only non-cloud sites + block: # block specifies execution of tasks within, based on conditions + - name: Add a new tenant + cisco.aci.aci_tenant: &aci_tenant_present + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + + - name: Add a Netflow Record policy + cisco.aci.aci_netflow_record_policy: + <<: *aci_info + tenant: ansible_tenant + netflow_record_policy: ansible_netflow_record_policy + description: Netflow Record policy for ansible_tenant tenant + state: present + + # CREATE NETFLOW MONITOR POLICY + - name: Add a Netflow Monitor policy (check_mode) + cisco.aci.aci_netflow_monitor_policy: &aci_netflow_monitor_policy_present + <<: *aci_info + tenant: ansible_tenant + netflow_monitor_policy: ansible_netflow_monitor_policy_1 + netflow_record_policy: ansible_netflow_record_policy + description: Netflow Monitor policy 1 for ansible_tenant tenant + state: present + check_mode: true + register: cm_add_netflow_monitor_policy + + - name: Add a Netflow Monitor policy (normal_mode) + cisco.aci.aci_netflow_monitor_policy: + <<: *aci_netflow_monitor_policy_present + register: nm_add_netflow_monitor_policy + + - name: Add the first Netflow Monitor policy again - testing idempotency + cisco.aci.aci_netflow_monitor_policy: + <<: *aci_netflow_monitor_policy_present + register: nm_add_netflow_monitor_policy_idempotency + + - name: Add a second Netflow Monitor policy (normal_mode) + cisco.aci.aci_netflow_monitor_policy: + <<: *aci_info + tenant: ansible_tenant + netflow_monitor_policy: ansible_netflow_monitor_policy_2 + description: Netflow Monitor policy 2 for ansible_tenant tenant + state: present + register: nm_add_netflow_monitor_policy_2 + + - name: Asserts for Netflow Monitor policy creation tasks + ansible.builtin.assert: + that: + - cm_add_netflow_monitor_policy is changed + - cm_add_netflow_monitor_policy.previous == [] + - cm_add_netflow_monitor_policy.current == [] + - nm_add_netflow_monitor_policy is changed + - nm_add_netflow_monitor_policy.current.0.netflowMonitorPol.attributes.name == "ansible_netflow_monitor_policy_1" + - nm_add_netflow_monitor_policy.current.0.netflowMonitorPol.children.0.netflowRsMonitorToRecord.attributes.tnNetflowRecordPolName == "ansible_netflow_record_policy" + - nm_add_netflow_monitor_policy_idempotency is not changed + - nm_add_netflow_monitor_policy_2 is changed + - nm_add_netflow_monitor_policy_2.previous == [] + - nm_add_netflow_monitor_policy_2.current.0.netflowMonitorPol.attributes.name == "ansible_netflow_monitor_policy_2" + + # QUERY NETFLOW MONITOR POLICY + - name: Query all Netflow Monitor policies + cisco.aci.aci_netflow_monitor_policy: + <<: *aci_info + state: query + register: query_all_netflow_monitor_policy + + - name: Query ansible_netflow_monitor_policy_1 + cisco.aci.aci_netflow_monitor_policy: + <<: *aci_netflow_monitor_policy_present + state: query + register: query_ansible_netflow_monitor_policy_1 + + - name: Asserts query tasks + ansible.builtin.assert: + that: + - query_all_netflow_monitor_policy is not changed + - query_all_netflow_monitor_policy.current|length >= 2 + - query_ansible_netflow_monitor_policy_1 is not changed + - query_ansible_netflow_monitor_policy_1.current.0.netflowMonitorPol.attributes.name == "ansible_netflow_monitor_policy_1" + - query_ansible_netflow_monitor_policy_1.current.0.netflowMonitorPol.children.0.netflowRsMonitorToRecord.attributes.tDn == "uni/tn-ansible_tenant/recordpol-ansible_netflow_record_policy" + + # DELETE NETFLOW MONITOR POLICY + - name: Remove Netflow Monitor policy (check_mode) + cisco.aci.aci_netflow_monitor_policy: &netflow_monitor_policy_absent + <<: *aci_netflow_monitor_policy_present + state: absent + check_mode: true + register: cm_remove_netflow_monitor_policy + + - name: Remove Netflow Monitor policy (normal_mode) + cisco.aci.aci_netflow_monitor_policy: + <<: *netflow_monitor_policy_absent + register: nm_remove_netflow_monitor_policy + + - name: Remove Netflow Monitor policy - testing idempotency + cisco.aci.aci_netflow_monitor_policy: + <<: *netflow_monitor_policy_absent + register: nm_remove_netflow_monitor_policy_idempotency + + - name: Asserts deletion tasks + ansible.builtin.assert: + that: + - cm_remove_netflow_monitor_policy is changed + - cm_remove_netflow_monitor_policy.proposed == {} + - nm_remove_netflow_monitor_policy is changed + - nm_remove_netflow_monitor_policy.previous != [] + - nm_remove_netflow_monitor_policy.current == [] + - nm_remove_netflow_monitor_policy_idempotency is not changed + - nm_remove_netflow_monitor_policy_idempotency.previous == [] + + # CLEAN ENVIRONMENT BEFORE ENDING TESTS + - name: Remove the ansible_tenant - cleanup before ending tests + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent diff --git a/tests/integration/targets/aci_netflow_record_policy/aliases b/tests/integration/targets/aci_netflow_record_policy/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_netflow_record_policy/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_netflow_record_policy/tasks/main.yml b/tests/integration/targets/aci_netflow_record_policy/tasks/main.yml new file mode 100644 index 000000000..c98236c7e --- /dev/null +++ b/tests/integration/targets/aci_netflow_record_policy/tasks/main.yml @@ -0,0 +1,178 @@ +# Test code for the ACI modules +# Copyright: (c) 2023, Gaspard Micol (@gmicol) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + ansible.builtin.fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + ansible.builtin.set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +# CLEAN ENVIRONMENT BEFORE TESTS +- name: Remove the ansible_tenant + cisco.aci.aci_tenant: &aci_tenant_absent + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Verify Cloud and Non-Cloud Sites in use. + ansible.builtin.include_tasks: ../../../../../../integration/targets/aci_cloud_provider/tasks/main.yml + +- name: Execute tasks only for non-cloud sites + when: query_cloud.current == [] # This condition will execute only non-cloud sites + block: # block specifies execution of tasks within, based on conditions + - name: Add a new tenant + cisco.aci.aci_tenant: &aci_tenant_present + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + + # CREATE NETFLOW RECORD POLICY + - name: Add a Netflow Record policy (check_mode) + cisco.aci.aci_netflow_record_policy: &aci_netflow_record_policy_present + <<: *aci_info + tenant: ansible_tenant + netflow_record_policy: ansible_netflow_record_policy_1 + description: Netflow Record policy 1 for ansible_tenant tenant + collect: [sampler_id, bytes_counter] + match: [destination_ipv4_v6, source_ipv4_v6] + state: present + check_mode: true + register: cm_add_netflow_record_policy + + - name: Add a Netflow Record policy (normal_mode) + cisco.aci.aci_netflow_record_policy: + <<: *aci_netflow_record_policy_present + register: nm_add_netflow_record_policy + + - name: Add the first Netflow Record policy again - testing idempotency + cisco.aci.aci_netflow_record_policy: + <<: *aci_netflow_record_policy_present + register: nm_add_netflow_record_policy_idempotency + + - name: Add a second Netflow Record policy (normal_mode) + cisco.aci.aci_netflow_record_policy: + <<: *aci_info + tenant: ansible_tenant + netflow_record_policy: ansible_netflow_record_policy_2 + description: Netflow Record policy 2 for ansible_tenant tenant + state: present + register: nm_add_netflow_record_policy_2 + + - name: Asserts for Netflow Record policy creation tasks + ansible.builtin.assert: + that: + - cm_add_netflow_record_policy is changed + - cm_add_netflow_record_policy.previous == [] + - cm_add_netflow_record_policy.current == [] + - nm_add_netflow_record_policy is changed + - nm_add_netflow_record_policy.current.0.netflowRecordPol.attributes.name == "ansible_netflow_record_policy_1" + - nm_add_netflow_record_policy.current.0.netflowRecordPol.attributes.collect == "count-bytes,sampler-id" + - nm_add_netflow_record_policy.current.0.netflowRecordPol.attributes.match == "dst-ip,src-ip" + - nm_add_netflow_record_policy_idempotency is not changed + - nm_add_netflow_record_policy_2 is changed + - nm_add_netflow_record_policy_2.previous == [] + - nm_add_netflow_record_policy_2.current.0.netflowRecordPol.attributes.name == "ansible_netflow_record_policy_2" + - nm_add_netflow_record_policy_2.current.0.netflowRecordPol.attributes.collect == "src-intf" + - nm_add_netflow_record_policy_2.current.0.netflowRecordPol.attributes.match == "" + + # QUERY NETFLOW RECORD POLICY + - name: Query all Netflow Record policies + cisco.aci.aci_netflow_record_policy: + <<: *aci_info + state: query + register: query_all_netflow_record_policy + + - name: Query ansible_netflow_record_policy_1 + cisco.aci.aci_netflow_record_policy: + <<: *aci_netflow_record_policy_present + state: query + register: query_ansible_netflow_record_policy_1 + + - name: Asserts query tasks + ansible.builtin.assert: + that: + - query_all_netflow_record_policy is not changed + - query_all_netflow_record_policy.current|length >= 2 + - query_ansible_netflow_record_policy_1 is not changed + - query_ansible_netflow_record_policy_1.current.0.netflowRecordPol.attributes.name == "ansible_netflow_record_policy_1" + - query_ansible_netflow_record_policy_1.current.0.netflowRecordPol.attributes.collect == "count-bytes,sampler-id" + - query_ansible_netflow_record_policy_1.current.0.netflowRecordPol.attributes.match == "dst-ip,src-ip" + + #UPDATE NETFLOW RECORD POLICY + - name: Update first Netflow Record policy (check_mode) + cisco.aci.aci_netflow_record_policy: &aci_netflow_record_policy_update + <<: *aci_netflow_record_policy_present + collect: [pkts_counter, pkt_disposition] + match: [destination_ipv4, source_ipv4] + state: present + check_mode: true + register: cm_update_netflow_record_policy + + - name: Update first Netflow Record policy (normal_mode) + cisco.aci.aci_netflow_record_policy: + <<: *aci_netflow_record_policy_update + register: nm_update_netflow_record_policy + + - name: Update first Netflow Record policy again - testing idempotency + cisco.aci.aci_netflow_record_policy: + <<: *aci_netflow_record_policy_update + register: nm_udpate_netflow_record_policy_idempotency + + - name: Asserts for Netflow Record policy update tasks + ansible.builtin.assert: + that: + - cm_update_netflow_record_policy is changed + - cm_update_netflow_record_policy.previous == cm_update_netflow_record_policy.current + - nm_update_netflow_record_policy is changed + - nm_update_netflow_record_policy.current.0.netflowRecordPol.attributes.name == "ansible_netflow_record_policy_1" + - nm_update_netflow_record_policy.current.0.netflowRecordPol.attributes.collect == "count-pkts,pkt-disp" + - nm_update_netflow_record_policy.current.0.netflowRecordPol.attributes.match == "dst-ipv4,src-ipv4" + - nm_udpate_netflow_record_policy_idempotency is not changed + + # DELETE NETFLOW RECORD POLICY + - name: Remove Netflow Record policy (check_mode) + cisco.aci.aci_netflow_record_policy: &netflow_record_policy_absent + <<: *aci_netflow_record_policy_update + state: absent + check_mode: true + register: cm_remove_netflow_record_policy + + - name: Remove Netflow Record policy (normal_mode) + cisco.aci.aci_netflow_record_policy: + <<: *netflow_record_policy_absent + register: nm_remove_netflow_record_policy + + - name: Remove Netflow Record policy - testing idempotency + cisco.aci.aci_netflow_record_policy: + <<: *netflow_record_policy_absent + register: nm_remove_netflow_record_policy_idempotency + + - name: Asserts deletion tasks + ansible.builtin.assert: + that: + - cm_remove_netflow_record_policy is changed + - cm_remove_netflow_record_policy.proposed == {} + - nm_remove_netflow_record_policy is changed + - nm_remove_netflow_record_policy.previous != [] + - nm_remove_netflow_record_policy.current == [] + - nm_remove_netflow_record_policy_idempotency is not changed + - nm_remove_netflow_record_policy_idempotency.previous == [] + + # CLEAN ENVIRONMENT BEFORE ENDING TESTS + - name: Remove the ansible_tenant - cleanup before ending tests + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent From 4756658d46b6d008c724ae43ee2ad246cf651977 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Fri, 12 Jan 2024 13:04:42 -0500 Subject: [PATCH 02/15] [ignore] Modify examples and modify conditions for aci_netflow_record_policy. --- plugins/modules/aci_netflow_monitor_policy.py | 1 + plugins/modules/aci_netflow_record_policy.py | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/plugins/modules/aci_netflow_monitor_policy.py b/plugins/modules/aci_netflow_monitor_policy.py index 75f2ce362..14164040b 100644 --- a/plugins/modules/aci_netflow_monitor_policy.py +++ b/plugins/modules/aci_netflow_monitor_policy.py @@ -69,6 +69,7 @@ password: SomeSecretPassword tenant: my_tenant netflow_monitor_policy: my_netflow_monitor_policy + netflow_record_policy: my_netflow_record_policy state: present delegate_to: localhost diff --git a/plugins/modules/aci_netflow_record_policy.py b/plugins/modules/aci_netflow_record_policy.py index 6e6f5f96f..e2f04af0c 100644 --- a/plugins/modules/aci_netflow_record_policy.py +++ b/plugins/modules/aci_netflow_record_policy.py @@ -39,7 +39,8 @@ - The match parameters for the flow record. type: list elements: str - choices: [ destination_ipv4_v6, destination_ipv4, destination_ipv6, destination_mac, destination_port, ethertype, ip_protocol, source_ipv4_v6, source_ipv4, source_ipv6, source_mac, source_port, ip_tos, unspecified, vlan ] + choices: [ destination_ipv4_v6, destination_ipv4, destination_ipv6, destination_mac, destination_port, ethertype, ip_protocol, source_ipv4_v6, + source_ipv4, source_ipv6, source_mac, source_port, ip_tos, unspecified, vlan ] description: description: - The description for the Netflow Record Policy. @@ -77,6 +78,8 @@ password: SomeSecretPassword tenant: my_tenant netflow_record_policy: my_netflow_record_policy + collect: [pkts_counter, pkt_disposition] + match: [destination_ipv4, source_ipv4] state: present delegate_to: localhost @@ -255,8 +258,16 @@ def main(): description = module.params.get("description") netflow_record_policy = module.params.get("netflow_record_policy") state = module.params.get("state") - collect = ",".join(sorted(MATCH_COLLECT_NETFLOW_RECORD_MAPPING.get(v) for v in module.params.get("collect"))) if module.params.get("collect") is not None else None - match = ",".join(sorted(MATCH_MATCH_NETFLOW_RECORD_MAPPING.get(v) for v in module.params.get("match"))) if module.params.get("match") is not None else None + + if module.params.get("collect") is not None: + collect = ",".join(sorted(MATCH_COLLECT_NETFLOW_RECORD_MAPPING.get(v) for v in module.params.get("collect"))) + else: + collect = None + + if module.params.get("match") is not None: + match = ",".join(sorted(MATCH_MATCH_NETFLOW_RECORD_MAPPING.get(v) for v in module.params.get("match"))) + else: + match = None aci = ACIModule(module) From 2e78816a083dc34cb736f8aa231d3cabec781012 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Fri, 12 Jan 2024 13:59:23 -0500 Subject: [PATCH 03/15] [minor_change] Add aci_netflow_exporter_policy as a new module. --- plugins/module_utils/constants.py | 7 + .../modules/aci_netflow_exporter_policy.py | 337 ++++++++++++++++++ 2 files changed, 344 insertions(+) create mode 100644 plugins/modules/aci_netflow_exporter_policy.py diff --git a/plugins/module_utils/constants.py b/plugins/module_utils/constants.py index aa5f9ad2b..010ef8860 100644 --- a/plugins/module_utils/constants.py +++ b/plugins/module_utils/constants.py @@ -221,3 +221,10 @@ unspecified="unspecified", vlan="vlan", ) + +MATCH_SOURCE_IP_TYPE_NETFLOW_EXPORTER_MAPPING = dict( + custom_source_ip="custom-src-ip", + inband_management_ip="inband-mgmt-ip", + out_of_band_management_ip="oob-mgmt-ip", + ptep="ptep", +) diff --git a/plugins/modules/aci_netflow_exporter_policy.py b/plugins/modules/aci_netflow_exporter_policy.py new file mode 100644 index 000000000..dcd10a83c --- /dev/null +++ b/plugins/modules/aci_netflow_exporter_policy.py @@ -0,0 +1,337 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2023, Gaspard Micol (@gmicol) +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"} + +DOCUMENTATION = r""" +--- +module: aci_netflow_exporter_policy +short_description: Manage Netflow Exporter Policy (netflow:ExporterPol) +description: +- Manage Netflow Exporter Policies for tenants on Cisco ACI fabrics. +- Exporter information for bootstrapping the netflow Collection agent. +options: + tenant: + description: + - The name of an existing tenant. + type: str + aliases: [ tenant_name ] + netflow_exporter_policy: + description: + - The name of the Netflow Exporter Policy. + type: str + aliases: [ netflow_exporter, netflow_exporter_name, name ] + dscp: + description: + - The IP DSCP value. + - The APIC defaults to C(CS2) when unset during creation. + type: str + choices: [ AF11, AF12, AF13, AF21, AF22, AF23, AF31, AF32, AF33, AF41, AF42, AF43, CS0, CS1, CS2, CS3, CS4, CS5, CS6, CS7, EF, VA, unspecified ] + destination_address: + description: + - The remote node destination IP address. + type: str + destination_port: + description: + - The remote node destination port. + - The port values range from 0 to 65535 included. + - The APIC defaults to C(0) (equivalent to C(unspecified)) when unset during creation. + type: str + source_ip_type: + description: + - The type of Exporter source IP Address. + - It Can be one of the available management IP Address for a given leaf or a custom IP Address. + type: str + choices: [ custom_source_ip, inband_management_ip, out_of_band_management_ip, ptep ] + custom_source_address: + description: + - The cutsom source IP address. + - It can only be used if I(source_ip_type) is C(custom_source_ip). + type: str + description: + description: + - The description for the Netflow Exporter Policy. + type: str + aliases: [ descr ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci +- cisco.aci.annotation +- cisco.aci.owner + +notes: +- The I(tenant) must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(netflow:ExporterPol). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Gaspard Micol (@gmicol) +""" + +EXAMPLES = r""" +- name: Add a new Netflow Exporter Policy + cisco.aci.aci_netflow_exporter_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + netflow_exporter_policy: my_netflow_exporter_policy + dscp: CS2 + destination_address: 11.11.11.1 + destination_port: 25 + source_ip_type: custom_source_ip + custom_source_address: 11.11.11.2 + state: present + delegate_to: localhost + +- name: Query a Netflow Exporter Policy + cisco.aci.aci_netflow_exporter_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + netflow_exporter_policy: my_netflow_exporter_policy + state: query + delegate_to: localhost + +- name: Query all Netflow Exporter Policies in my_tenant + cisco.aci.aci_netflow_exporter_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + state: query + delegate_to: localhost + +- name: Query all Netflow Exporter Policies + cisco.aci.aci_netflow_exporter_policy: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + +- name: Delete a Netflow Exporter Policy + cisco.aci.aci_netflow_exporter_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + netflow_exporter_policy: my_netflow_exporter_policy + state: absent + delegate_to: localhost +""" + +RETURN = r""" +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ( + ACIModule, aci_argument_spec, + aci_annotation_spec, + aci_owner_spec, + aci_contract_dscp_spec +) +from ansible_collections.cisco.aci.plugins.module_utils.constants import MATCH_SOURCE_IP_TYPE_NETFLOW_EXPORTER_MAPPING + + +def main(): + new_dscp_spec = dict((k, aci_contract_dscp_spec()[k]) for k in aci_contract_dscp_spec() if k != "aliases") + argument_spec = aci_argument_spec() + argument_spec.update(aci_annotation_spec()) + argument_spec.update(aci_owner_spec()) + argument_spec.update( + tenant=dict(type="str", aliases=["tenant_name"]), + netflow_exporter_policy=dict(type="str", aliases=["netflow_exporter", "netflow_exporter_name", "name"]), + dscp=new_dscp_spec, + destination_address=dict(type="str"), + destination_port=dict(type="str"), + source_ip_type=dict(type="str", choices=list(MATCH_SOURCE_IP_TYPE_NETFLOW_EXPORTER_MAPPING.keys())), + custom_source_address=dict(type="str"), + description=dict(type="str", aliases=["descr"]), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "absent", ["tenant", "netflow_exporter_policy"]], + ["state", "present", ["tenant", "netflow_exporter_policy", "destination_address", "destination_port"]], + ], + ) + + tenant = module.params.get("tenant") + description = module.params.get("description") + netflow_exporter_policy = module.params.get("netflow_exporter_policy") + dscp = module.params.get("dscp") + destination_address = module.params.get("destination_address") + destination_port = module.params.get("destination_port") + source_ip_type = MATCH_SOURCE_IP_TYPE_NETFLOW_EXPORTER_MAPPING.get(module.params.get("source_ip_type")) + custom_source_address = module.params.get("custom_source_address") + state = module.params.get("state") + + aci = ACIModule(module) + + aci.construct_url( + root_class=dict( + aci_class="fvTenant", + aci_rn="tn-{0}".format(tenant), + module_object=tenant, + target_filter={"name": tenant}, + ), + subclass_1=dict( + aci_class="netflowExporterPol", + aci_rn="exporterpol-{0}".format(netflow_exporter_policy), + module_object=netflow_exporter_policy, + target_filter={"name": netflow_exporter_policy}, + ), + ) + + aci.get_existing() + + if state == "present": + aci.payload( + aci_class="netflowExporterPol", + class_config=dict( + name=netflow_exporter_policy, + descr=description, + dscp=dscp, + dstAddr=destination_address, + dstPort=destination_port, + sourceIpType=source_ip_type, + srcAddr=custom_source_address, + ), + ) + + aci.get_diff(aci_class="netflowExporterPol") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() From 388da1f782460e19ea3e996202efb76169461d7e Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Fri, 12 Jan 2024 14:05:35 -0500 Subject: [PATCH 04/15] [ignore] Apply Black to aci_netflow_exporter_policy module. --- plugins/modules/aci_netflow_exporter_policy.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/plugins/modules/aci_netflow_exporter_policy.py b/plugins/modules/aci_netflow_exporter_policy.py index dcd10a83c..bdac82601 100644 --- a/plugins/modules/aci_netflow_exporter_policy.py +++ b/plugins/modules/aci_netflow_exporter_policy.py @@ -245,12 +245,7 @@ from ansible.module_utils.basic import AnsibleModule -from ansible_collections.cisco.aci.plugins.module_utils.aci import ( - ACIModule, aci_argument_spec, - aci_annotation_spec, - aci_owner_spec, - aci_contract_dscp_spec -) +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec, aci_contract_dscp_spec from ansible_collections.cisco.aci.plugins.module_utils.constants import MATCH_SOURCE_IP_TYPE_NETFLOW_EXPORTER_MAPPING From 076e2bb5cc5a6f7e76e9035cbad3ae83b8d522ff Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Tue, 16 Jan 2024 12:51:40 -0500 Subject: [PATCH 05/15] [minor_change] Add aci_netflow_monitor_to_exporter as a new module and add integration test for aci_netflow_exporter_policy. --- .../modules/aci_netflow_exporter_policy.py | 4 +- .../aci_netflow_monitor_to_exporter.py | 290 ++++++++++++++++++ .../aci_netflow_exporter_policy/aliases | 2 + .../tasks/main.yml | 196 ++++++++++++ .../aci_netflow_monitor_to_exporter/aliases | 2 + .../tasks/main.yml | 167 ++++++++++ 6 files changed, 659 insertions(+), 2 deletions(-) create mode 100644 plugins/modules/aci_netflow_monitor_to_exporter.py create mode 100644 tests/integration/targets/aci_netflow_exporter_policy/aliases create mode 100644 tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml create mode 100644 tests/integration/targets/aci_netflow_monitor_to_exporter/aliases create mode 100644 tests/integration/targets/aci_netflow_monitor_to_exporter/tasks/main.yml diff --git a/plugins/modules/aci_netflow_exporter_policy.py b/plugins/modules/aci_netflow_exporter_policy.py index bdac82601..4abf329a3 100644 --- a/plugins/modules/aci_netflow_exporter_policy.py +++ b/plugins/modules/aci_netflow_exporter_policy.py @@ -32,6 +32,7 @@ description: - The IP DSCP value. - The APIC defaults to C(CS2) when unset during creation. + It defaults to C(VA) for APIC versions 4.2 or prior. type: str choices: [ AF11, AF12, AF13, AF21, AF22, AF23, AF31, AF32, AF33, AF41, AF42, AF43, CS0, CS1, CS2, CS3, CS4, CS5, CS6, CS7, EF, VA, unspecified ] destination_address: @@ -41,8 +42,7 @@ destination_port: description: - The remote node destination port. - - The port values range from 0 to 65535 included. - - The APIC defaults to C(0) (equivalent to C(unspecified)) when unset during creation. + - The APIC defaults to C(unspecified) when unset during creation. type: str source_ip_type: description: diff --git a/plugins/modules/aci_netflow_monitor_to_exporter.py b/plugins/modules/aci_netflow_monitor_to_exporter.py new file mode 100644 index 000000000..804c77241 --- /dev/null +++ b/plugins/modules/aci_netflow_monitor_to_exporter.py @@ -0,0 +1,290 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2023, Gaspard Micol (@gmicol) +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"} + +DOCUMENTATION = r""" +--- +module: aci_netflow_monitor_to_exporter +short_description: Manage Netflow Monitor to Exporter (netflow:RsMonitorToExporter) +description: +- Link Netflow Exporter policies to Netflow Monitor policies for tenants on Cisco ACI fabrics. +options: + tenant: + description: + - The name of an existing tenant. + type: str + aliases: [ tenant_name ] + netflow_monitor_policy: + description: + - The name of the Netflow Monitor Policy. + type: str + aliases: [ netflow_monitor, netflow_monitor_name, name ] + netflow_exporter_policy: + description: + - The name of the Netflow Exporter Policy. + type: str + aliases: [ netflow_exporter, netflow_exporter_name ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci +- cisco.aci.annotation +- cisco.aci.owner + +notes: +- The I(tenant), I(netflow_monitor_policy) and I(netflow_exporter_policy) must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant), M(cisco.aci.aci_netflow_monitor_policy), M(cisco.aci.aci_netflow_exporter_policy) can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- module: cisco.aci.aci_netflow_monitor_policy +- module: cisco.aci.aci_netflow_exporter_policy +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(netflow:RsMonitorToExporter). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Gaspard Micol (@gmicol) +""" + +EXAMPLES = r""" +- name: Add a new Netflow Monitor Policy + cisco.aci.aci_netflow_monitor_to_exporter: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + netflow_monitor_policy: my_netflow_monitor_policy + netflow_exporter_policy: my_netflow_exporter_policy + state: present + delegate_to: localhost + +- name: Query a Netflow Monitor Policy + cisco.aci.aci_netflow_monitor_to_exporter: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + netflow_monitor_policy: my_netflow_monitor_policy + netflow_exporter_policy: my_netflow_exporter_policy + state: query + delegate_to: localhost + +- name: Query all Netflow Monitor Policies in my_tenant + cisco.aci.aci_netflow_monitor_to_exporter: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + state: query + delegate_to: localhost + +- name: Query all Netflow Monitor Policies + cisco.aci.aci_netflow_monitor_to_exporter: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + +- name: Delete a Netflow Monitor Policy + cisco.aci.aci_netflow_monitor_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + netflow_monitor_policy: my_netflow_monitor_policy + netflow_exporter_policy: my_netflow_exporter_policy + state: absent + delegate_to: localhost +""" + +RETURN = r""" +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update(aci_annotation_spec()) + argument_spec.update(aci_owner_spec()) + argument_spec.update( + tenant=dict(type="str", aliases=["tenant_name"]), + netflow_monitor_policy=dict(type="str", aliases=["netflow_monitor", "netflow_monitor_name", "name"]), + netflow_exporter_policy=dict(type="str", aliases=["netflow_exporter", "netflow_exporter_name"]), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "absent", ["tenant", "netflow_monitor_policy", "netflow_exporter_policy"]], + ["state", "present", ["tenant", "netflow_monitor_policy", "netflow_exporter_policy"]], + ], + ) + + tenant = module.params.get("tenant") + netflow_monitor_policy = module.params.get("netflow_monitor_policy") + netflow_exporter_policy = module.params.get("netflow_exporter_policy") + state = module.params.get("state") + + aci = ACIModule(module) + + aci.construct_url( + root_class=dict( + aci_class="fvTenant", + aci_rn="tn-{0}".format(tenant), + module_object=tenant, + target_filter={"name": tenant}, + ), + subclass_1=dict( + aci_class="netflowMonitorPol", + aci_rn="monitorpol-{0}".format(netflow_monitor_policy), + module_object=netflow_monitor_policy, + target_filter={"name": netflow_monitor_policy}, + ), + subclass_2=dict( + aci_class="netflowRsMonitorToExporter", + aci_rn="rsmonitorToExporter-{0}".format(netflow_exporter_policy), + module_object=netflow_exporter_policy, + target_filter={"tnNetflowExporterPolName": netflow_exporter_policy}, + ), + ) + + aci.get_existing() + + if state == "present": + aci.payload( + aci_class="netflowRsMonitorToExporter", + class_config=dict(tnNetflowExporterPolName=netflow_exporter_policy), + ) + + aci.get_diff(aci_class="netflowRsMonitorToExporter") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/aci_netflow_exporter_policy/aliases b/tests/integration/targets/aci_netflow_exporter_policy/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_netflow_exporter_policy/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml b/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml new file mode 100644 index 000000000..d07f1de73 --- /dev/null +++ b/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml @@ -0,0 +1,196 @@ +# Test code for the ACI modules +# Copyright: (c) 2023, Gaspard Micol (@gmicol) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + ansible.builtin.fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + ansible.builtin.set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +# CLEAN ENVIRONMENT BEFORE TESTS +- name: Remove the ansible_tenant + cisco.aci.aci_tenant: &aci_tenant_absent + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Verify Cloud and Non-Cloud Sites in use. + ansible.builtin.include_tasks: ../../../../../../integration/targets/aci_cloud_provider/tasks/main.yml + +- name: Execute tasks only for non-cloud sites + when: query_cloud.current == [] # This condition will execute only non-cloud sites + block: # block specifies execution of tasks within, based on conditions + - name: Add a new tenant + cisco.aci.aci_tenant: &aci_tenant_present + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + + # CREATE NETFLOW EXPORTER POLICY + - name: Add a Netflow Exporter policy (check_mode) + cisco.aci.aci_netflow_exporter_policy: &aci_netflow_exporter_policy_present + <<: *aci_info + tenant: ansible_tenant + netflow_exporter_policy: ansible_netflow_exporter_policy_1 + description: Netflow Exporter policy 1 for ansible_tenant tenant + dscp: AF12 + destination_address: 11.11.11.1 + destination_port: smtp + source_ip_type: inband_management_ip + state: present + check_mode: true + register: cm_add_netflow_exporter_policy + + - name: Add a Netflow Exporter policy (normal_mode) + cisco.aci.aci_netflow_exporter_policy: + <<: *aci_netflow_exporter_policy_present + register: nm_add_netflow_exporter_policy + + - name: Add the first Netflow Exporter policy again - testing idempotency + cisco.aci.aci_netflow_exporter_policy: + <<: *aci_netflow_exporter_policy_present + register: nm_add_netflow_exporter_policy_idempotency + + - name: Add a second Netflow Exporter policy (normal_mode) + cisco.aci.aci_netflow_exporter_policy: + <<: *aci_info + tenant: ansible_tenant + netflow_exporter_policy: ansible_netflow_exporter_policy_2 + description: Netflow Exporter policy 2 for ansible_tenant tenant + dscp: CS2 + destination_address: 11.11.11.2 + destination_port: https + custom_source_address: 12.12.12.2/12 + state: present + register: nm_add_netflow_exporter_policy_2 + + - name: Asserts for Netflow Exporter policy creation tasks + ansible.builtin.assert: + that: + - cm_add_netflow_exporter_policy is changed + - cm_add_netflow_exporter_policy.previous == [] + - cm_add_netflow_exporter_policy.current == [] + - nm_add_netflow_exporter_policy is changed + - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.attributes.name == "ansible_netflow_exporter_policy_1" + - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.attributes.dscp == "AF12" + - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.attributes.dstAddr == "11.11.11.1" + - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.attributes.dstPort == "smtp" + - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.attributes.sourceIpType == "inband-mgmt-ip" + - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.attributes.srcAddr == "0.0.0.0" + - nm_add_netflow_exporter_policy_idempotency is not changed + - nm_add_netflow_exporter_policy_2 is changed + - nm_add_netflow_exporter_policy_2.previous == [] + - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.attributes.name == "ansible_netflow_exporter_policy_2" + - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.attributes.dscp == "CS2" + - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.attributes.dstAddr == "11.11.11.2" + - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.attributes.dstPort == "https" + - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.attributes.sourceIpType == "custom-src-ip" + - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.attributes.srcAddr == "12.12.12.2/12" + + # QUERY NETFLOW Exporter POLICY + - name: Query all Netflow Exporter policies + cisco.aci.aci_netflow_exporter_policy: + <<: *aci_info + state: query + register: query_all_netflow_exporter_policy + + - name: Query ansible_netflow_exporter_policy_1 + cisco.aci.aci_netflow_exporter_policy: + <<: *aci_netflow_exporter_policy_present + state: query + register: query_ansible_netflow_exporter_policy_1 + + - name: Asserts query tasks + ansible.builtin.assert: + that: + - query_all_netflow_exporter_policy is not changed + - query_all_netflow_exporter_policy.current|length >= 2 + - query_ansible_netflow_exporter_policy_1 is not changed + - query_ansible_netflow_exporter_policy_1.current.0.netflowExporterPol.attributes.name == "ansible_netflow_exporter_policy_1" + - query_ansible_netflow_exporter_policy_1.current.0.netflowExporterPol.attributes.dscp == "AF12" + - query_ansible_netflow_exporter_policy_1.current.0.netflowExporterPol.attributes.dstAddr == "11.11.11.1" + - query_ansible_netflow_exporter_policy_1.current.0.netflowExporterPol.attributes.dstPort == "smtp" + - query_ansible_netflow_exporter_policy_1.current.0.netflowExporterPol.attributes.sourceIpType == "inband-mgmt-ip" + - query_ansible_netflow_exporter_policy_1.current.0.netflowExporterPol.attributes.srcAddr == "0.0.0.0" + + #UPDATE NETFLOW EXPORTER POLICY + - name: Update first Netflow Exporter policy (check_mode) + cisco.aci.aci_netflow_exporter_policy: &aci_netflow_exporter_policy_update + <<: *aci_netflow_exporter_policy_present + dscp: AF13 + destination_address: 11.11.11.3 + destination_port: http + source_ip_type: out_of_band_management_ip + check_mode: true + register: cm_update_netflow_exporter_policy + + - name: Update first Netflow Exporter policy (normal_mode) + cisco.aci.aci_netflow_exporter_policy: + <<: *aci_netflow_exporter_policy_update + register: nm_update_netflow_exporter_policy + + - name: Update first Netflow Exporter policy again - testing idempotency + cisco.aci.aci_netflow_exporter_policy: + <<: *aci_netflow_exporter_policy_update + register: nm_udpate_netflow_exporter_policy_idempotency + + - name: Asserts for Netflow Exporter policy update tasks + ansible.builtin.assert: + that: + - cm_update_netflow_exporter_policy is changed + - cm_update_netflow_exporter_policy.previous == cm_update_netflow_exporter_policy.current + - nm_update_netflow_exporter_policy is changed + - nm_update_netflow_exporter_policy.current.0.netflowExporterPol.attributes.dscp == "AF13" + - nm_update_netflow_exporter_policy.current.0.netflowExporterPol.attributes.dstAddr == "11.11.11.3" + - nm_update_netflow_exporter_policy.current.0.netflowExporterPol.attributes.dstPort == "http" + - nm_update_netflow_exporter_policy.current.0.netflowExporterPol.attributes.sourceIpType == "oob-mgmt-ip" + - nm_update_netflow_exporter_policy.current.0.netflowExporterPol.attributes.srcAddr == "0.0.0.0" + - nm_udpate_netflow_exporter_policy_idempotency is not changed + + # DELETE NETFLOW EXPORTER POLICY + - name: Remove Netflow Exporter policy (check_mode) + cisco.aci.aci_netflow_exporter_policy: &netflow_exporter_policy_absent + <<: *aci_netflow_exporter_policy_update + state: absent + check_mode: true + register: cm_remove_netflow_exporter_policy + + - name: Remove Netflow Exporter policy (normal_mode) + cisco.aci.aci_netflow_exporter_policy: + <<: *netflow_exporter_policy_absent + register: nm_remove_netflow_exporter_policy + + - name: Remove Netflow Exporter policy - testing idempotency + cisco.aci.aci_netflow_exporter_policy: + <<: *netflow_exporter_policy_absent + register: nm_remove_netflow_exporter_policy_idempotency + + - name: Asserts deletion tasks + ansible.builtin.assert: + that: + - cm_remove_netflow_exporter_policy is changed + - cm_remove_netflow_exporter_policy.proposed == {} + - nm_remove_netflow_exporter_policy is changed + - nm_remove_netflow_exporter_policy.previous != [] + - nm_remove_netflow_exporter_policy.current == [] + - nm_remove_netflow_exporter_policy_idempotency is not changed + - nm_remove_netflow_exporter_policy_idempotency.previous == [] + + # CLEAN ENVIRONMENT BEFORE ENDING TESTS + - name: Remove the ansible_tenant - cleanup before ending tests + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent diff --git a/tests/integration/targets/aci_netflow_monitor_to_exporter/aliases b/tests/integration/targets/aci_netflow_monitor_to_exporter/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_netflow_monitor_to_exporter/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_netflow_monitor_to_exporter/tasks/main.yml b/tests/integration/targets/aci_netflow_monitor_to_exporter/tasks/main.yml new file mode 100644 index 000000000..ae695f108 --- /dev/null +++ b/tests/integration/targets/aci_netflow_monitor_to_exporter/tasks/main.yml @@ -0,0 +1,167 @@ +# Test code for the ACI modules +# Copyright: (c) 2023, Gaspard Micol (@gmicol) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + ansible.builtin.fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + ansible.builtin.set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: '{{ aci_output_level | default("info") }}' + +# CLEAN ENVIRONMENT BEFORE TESTS +- name: Remove the ansible_tenant + cisco.aci.aci_tenant: &aci_tenant_absent + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Verify Cloud and Non-Cloud Sites in use. + ansible.builtin.include_tasks: ../../../../../../integration/targets/aci_cloud_provider/tasks/main.yml + +- name: Execute tasks only for non-cloud sites + when: query_cloud.current == [] # This condition will execute only non-cloud sites + block: # block specifies execution of tasks within, based on conditions + - name: Add a new tenant + cisco.aci.aci_tenant: &aci_tenant_present + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + + - name: Add a Netflow Monitor policy + cisco.aci.aci_netflow_monitor_policy: + <<: *aci_info + tenant: ansible_tenant + netflow_monitor_policy: ansible_netflow_monitor_policy + description: Netflow Monitor policy for ansible_tenant tenant + state: present + + - name: Add a first Netflow Exporter policy + cisco.aci.aci_netflow_exporter_policy: + <<: *aci_info + tenant: ansible_tenant + netflow_exporter_policy: ansible_netflow_exporter_policy_1 + description: Netflow Exporter policy 1 for ansible_tenant tenant + destination_address: 11.11.11.1 + destination_port: https + state: present + + - name: Add a second Netflow Exporter policy + cisco.aci.aci_netflow_exporter_policy: + <<: *aci_info + tenant: ansible_tenant + netflow_exporter_policy: ansible_netflow_exporter_policy_2 + description: Netflow Exporter policy 2 for ansible_tenant tenant + destination_address: 12.12.12.2 + destination_port: http + state: present + + # CREATE NETFLOW MONITOR TO EXPORTER + - name: Add a first Netflow Exporter policy to the Netflow Monitor policy (check_mode) + cisco.aci.aci_netflow_monitor_to_exporter: &aci_netflow_monitor_to_exporter_present + <<: *aci_info + tenant: ansible_tenant + netflow_monitor_policy: ansible_netflow_monitor_policy + netflow_exporter_policy: ansible_netflow_exporter_policy_1 + state: present + check_mode: true + register: cm_add_netflow_monitor_to_exporter + + - name: Add a first Netflow Exporter policy to the Netflow Monitor policy (normal_mode) + cisco.aci.aci_netflow_monitor_to_exporter: + <<: *aci_netflow_monitor_to_exporter_present + register: nm_add_netflow_monitor_to_exporter + + - name: Add the first Netflow Monitor policy again - testing idempotency + cisco.aci.aci_netflow_monitor_to_exporter: + <<: *aci_netflow_monitor_to_exporter_present + register: nm_add_netflow_monitor_to_exporter_idempotency + + - name: Add a second Netflow Monitor policy (normal_mode) + cisco.aci.aci_netflow_monitor_to_exporter: + <<: *aci_info + tenant: ansible_tenant + netflow_monitor_policy: ansible_netflow_monitor_policy + netflow_exporter_policy: ansible_netflow_exporter_policy_2 + state: present + register: nm_add_netflow_monitor_to_exporter_2 + + - name: Asserts for Netflow Monitor policy creation tasks + ansible.builtin.assert: + that: + - cm_add_netflow_monitor_to_exporter is changed + - cm_add_netflow_monitor_to_exporter.previous == [] + - cm_add_netflow_monitor_to_exporter.current == [] + - nm_add_netflow_monitor_to_exporter is changed + - nm_add_netflow_monitor_to_exporter.current.0.netflowRsMonitorToExporter.attributes.tnNetflowExporterPolName == "ansible_netflow_exporter_policy_1" + - nm_add_netflow_monitor_to_exporter_idempotency is not changed + - nm_add_netflow_monitor_to_exporter_2 is changed + - nm_add_netflow_monitor_to_exporter_2.previous == [] + - nm_add_netflow_monitor_to_exporter_2.current.0.netflowRsMonitorToExporter.attributes.tnNetflowExporterPolName == "ansible_netflow_exporter_policy_2" + + # QUERY NETFLOW MONITOR TO EXPORTER + - name: Query all Netflow Monitor to Exporter Associations + cisco.aci.aci_netflow_monitor_to_exporter: + <<: *aci_info + state: query + register: query_all_netflow_monitor_to_exporter + + - name: Query first Netflow Monitor to Exporter Association + cisco.aci.aci_netflow_monitor_to_exporter: + <<: *aci_netflow_monitor_to_exporter_present + state: query + register: query_ansible_netflow_monitor_policy_to_exporter + + - name: Asserts query tasks + ansible.builtin.assert: + that: + - query_all_netflow_monitor_to_exporter is not changed + - query_all_netflow_monitor_to_exporter.current|length >= 2 + - query_ansible_netflow_monitor_policy_to_exporter is not changed + - query_ansible_netflow_monitor_policy_to_exporter.current.0.netflowRsMonitorToExporter.attributes.tnNetflowExporterPolName == "ansible_netflow_exporter_policy_1" + + # DELETE NETFLOW MONITOR TO EXPORTER + - name: Remove first Netflow Exporter policy from the Netflow Monitor policy (check_mode) + cisco.aci.aci_netflow_monitor_to_exporter: &netflow_monitor_to_exporter_absent + <<: *aci_netflow_monitor_to_exporter_present + state: absent + check_mode: true + register: cm_remove_netflow_monitor_to_exporter + + - name: Remove first Netflow Exporter policy from the Netflow Monitor policy (normal_mode) + cisco.aci.aci_netflow_monitor_to_exporter: + <<: *netflow_monitor_to_exporter_absent + register: nm_remove_netflow_monitor_to_exporter + + - name: Remove first Netflow Exporter policy from the Netflow Monitor policy - testing idempotency + cisco.aci.aci_netflow_monitor_to_exporter: + <<: *netflow_monitor_to_exporter_absent + register: nm_remove_netflow_monitor_to_exporter_idempotency + + - name: Asserts deletion tasks + ansible.builtin.assert: + that: + - cm_remove_netflow_monitor_to_exporter is changed + - cm_remove_netflow_monitor_to_exporter.proposed == {} + - nm_remove_netflow_monitor_to_exporter is changed + - nm_remove_netflow_monitor_to_exporter.previous != [] + - nm_remove_netflow_monitor_to_exporter.current == [] + - nm_remove_netflow_monitor_to_exporter_idempotency is not changed + - nm_remove_netflow_monitor_to_exporter_idempotency.previous == [] + + # CLEAN ENVIRONMENT BEFORE ENDING TESTS + - name: Remove the ansible_tenant - cleanup before ending tests + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent From bf7742d57883baffcf01bd479fb00e3f6c6cc4e0 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Wed, 17 Jan 2024 13:57:21 -0500 Subject: [PATCH 06/15] [ignore] Add conditions to associate an EPG or extEPG to Netflow Exporter Policy. --- plugins/module_utils/aci.py | 15 ++++ .../modules/aci_netflow_exporter_policy.py | 87 ++++++++++++++++++- 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 04f387dd8..3db5aff83 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -336,6 +336,21 @@ def action_rule_set_dampening_spec(): suppress=dict(type="int"), ) +def associated_netflow_exporter_epg_spec(): + return dict( + tenant=dict(type="str", required=True), + vrf=dict(type="str", required=True), + ap=dict(type="str", required=True), + epg=dict(type="str"), + ) + +def associated_netflow_exporter_extepg_spec(): + return dict( + tenant=dict(type="str", required=True), + vrf=dict(type="str", required=True), + l3out=dict(type="str", required=True), + extepg=dict(type="str"), + ) class ACIModule(object): def __init__(self, module): diff --git a/plugins/modules/aci_netflow_exporter_policy.py b/plugins/modules/aci_netflow_exporter_policy.py index 4abf329a3..567ab9ddd 100644 --- a/plugins/modules/aci_netflow_exporter_policy.py +++ b/plugins/modules/aci_netflow_exporter_policy.py @@ -42,6 +42,7 @@ destination_port: description: - The remote node destination port. + - Accepted values are any valid TCP/UDP port range. - The APIC defaults to C(unspecified) when unset during creation. type: str source_ip_type: @@ -55,6 +56,42 @@ - The cutsom source IP address. - It can only be used if I(source_ip_type) is C(custom_source_ip). type: str + associated_epg: + description: + - The associated EPG. + type: dict + suboptions: + tenant: + description: + - The name of the tenant to which the associated AP/EPG and VRF belong. + type: str + vrf: + description: + - The name of the associated VRF. + ap: + description: + - The name of the associated Application Profile to which the associated EPG belongs. + epg: + description: + - The name of the associated EPG. + associated_extepg: + description: + - The name of the associated external EPG. + type: dict + suboptions: + tenant: + description: + - The name of the tenant to which the associated L3Out/external EPG and VRF belong. + type: str + vrf: + description: + - The name of the associated VRF. + l3out: + description: + - The name of the L3Out to which the associated external EPG belongs. + extepg: + description: + - The name of the associated EPG. description: description: - The description for the Netflow Exporter Policy. @@ -245,7 +282,15 @@ from ansible.module_utils.basic import AnsibleModule -from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec, aci_contract_dscp_spec +from ansible_collections.cisco.aci.plugins.module_utils.aci import ( + ACIModule, + aci_argument_spec, + aci_annotation_spec, + aci_owner_spec, + aci_contract_dscp_spec, + associated_netflow_exporter_epg_spec, + associated_netflow_exporter_extepg_spec, +) from ansible_collections.cisco.aci.plugins.module_utils.constants import MATCH_SOURCE_IP_TYPE_NETFLOW_EXPORTER_MAPPING @@ -262,6 +307,8 @@ def main(): destination_port=dict(type="str"), source_ip_type=dict(type="str", choices=list(MATCH_SOURCE_IP_TYPE_NETFLOW_EXPORTER_MAPPING.keys())), custom_source_address=dict(type="str"), + associated_epg=dict(type="dict", options=associated_netflow_exporter_epg_spec()), + associated_extepg=dict(type="dict", options=associated_netflow_exporter_extepg_spec()), description=dict(type="str", aliases=["descr"]), state=dict(type="str", default="present", choices=["absent", "present", "query"]), ) @@ -273,6 +320,9 @@ def main(): ["state", "absent", ["tenant", "netflow_exporter_policy"]], ["state", "present", ["tenant", "netflow_exporter_policy", "destination_address", "destination_port"]], ], + mutually_exclusive=[ + ("associated_epg", "associated_extepg") + ] ) tenant = module.params.get("tenant") @@ -283,10 +333,14 @@ def main(): destination_port = module.params.get("destination_port") source_ip_type = MATCH_SOURCE_IP_TYPE_NETFLOW_EXPORTER_MAPPING.get(module.params.get("source_ip_type")) custom_source_address = module.params.get("custom_source_address") + associated_epg = module.params.get("associated_epg") + associated_extepg = module.params.get("associated_extepg") state = module.params.get("state") aci = ACIModule(module) + child_classes = ["netflowRsExporterToCtx", "netflowRsExporterToEPg"] + aci.construct_url( root_class=dict( aci_class="fvTenant", @@ -300,11 +354,41 @@ def main(): module_object=netflow_exporter_policy, target_filter={"name": netflow_exporter_policy}, ), + child_classes=child_classes, ) aci.get_existing() if state == "present": + if associated_epg is not None: + if all(value is None for value in associated_epg.values()) and isinstance(aci.existing, list) and len(aci.existing) > 0: + for child in aci.existing[0].get("netflowExporterPol", {}).get("children", {}): + if child.get("netflowRsExporterToCtx") and child.get("netflowRsExporterToEPg"): + child_configs = [ + dict(netflowRsExporterToCtx=dict(attributes=dict(status="deleted"))), + dict(netflowRsExporterToEPg=dict(attributes=dict(status="deleted"))), + ] + elif all(value is not None for value in associated_epg.values()): + associated_tenant = associated_epg.get("tenant") + child_configs = [ + dict(netflowRsExporterToCtx=dict(attributes=dict(tDn="uni/tn-{0}/ctx-{1}".format(associated_tenant, associated_epg.get("vrf"))))), + dict(netflowRsExporterToEPg=dict(attributes=dict(tDn="uni/tn-{0}/ap-{1}/epg-{2}".format(associated_tenant, associated_epg.get("ap"), associated_epg.get("epg"))))), + ] + elif associated_extepg is not None: + if all(value is None for value in associated_extepg.values()) and isinstance(aci.existing, list) and len(aci.existing) > 0: + for child in aci.existing[0].get("netflowExporterPol", {}).get("children", {}): + if child.get("netflowRsExporterToCtx") and child.get("netflowRsExporterToEPg"): + child_configs = [ + dict(netflowRsExporterToCtx=dict(attributes=dict(status="deleted"))), + dict(netflowRsExporterToEPg=dict(attributes=dict(status="deleted"))), + ] + elif all(value is not None for value in associated_extepg.values()): + associated_tenant = associated_extepg.get("tenant") + child_configs = [ + dict(netflowRsExporterToCtx=dict(attributes=dict(tDn="uni/tn-{0}/ctx-{1}".format(associated_tenant, associated_extepg.get("vrf"))))), + dict(netflowRsExporterToEPg=dict(attributes=dict(tDn="uni/tn-{0}/out-{1}/instP-{2}".format(associated_tenant, associated_extepg.get("l3out"), associated_extepg.get("extepg"))))), + ] + aci.payload( aci_class="netflowExporterPol", class_config=dict( @@ -316,6 +400,7 @@ def main(): sourceIpType=source_ip_type, srcAddr=custom_source_address, ), + child_configs=child_configs, ) aci.get_diff(aci_class="netflowExporterPol") From 58406f03d60c5126115c8724b629ba87fe69599c Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Thu, 18 Jan 2024 12:35:55 -0500 Subject: [PATCH 07/15] [ignore] Add EPG association tasks in integration test for aci_netflow_exporter_policy. --- plugins/module_utils/aci.py | 12 +- .../modules/aci_netflow_exporter_policy.py | 43 ++++--- .../tasks/main.yml | 114 +++++++++++++++++- 3 files changed, 146 insertions(+), 23 deletions(-) diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 3db5aff83..b0959ad23 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -338,17 +338,17 @@ def action_rule_set_dampening_spec(): def associated_netflow_exporter_epg_spec(): return dict( - tenant=dict(type="str", required=True), - vrf=dict(type="str", required=True), - ap=dict(type="str", required=True), + tenant=dict(type="str"), + vrf=dict(type="str"), + ap=dict(type="str"), epg=dict(type="str"), ) def associated_netflow_exporter_extepg_spec(): return dict( - tenant=dict(type="str", required=True), - vrf=dict(type="str", required=True), - l3out=dict(type="str", required=True), + tenant=dict(type="str"), + vrf=dict(type="str"), + l3out=dict(type="str"), extepg=dict(type="str"), ) diff --git a/plugins/modules/aci_netflow_exporter_policy.py b/plugins/modules/aci_netflow_exporter_policy.py index 567ab9ddd..64d000d73 100644 --- a/plugins/modules/aci_netflow_exporter_policy.py +++ b/plugins/modules/aci_netflow_exporter_policy.py @@ -59,6 +59,7 @@ associated_epg: description: - The associated EPG. + - To Remove the current associated EPG, pass an empty dictionary. type: dict suboptions: tenant: @@ -76,7 +77,8 @@ - The name of the associated EPG. associated_extepg: description: - - The name of the associated external EPG. + - The associated external EPG. + - To Remove the current associated external EPG, pass an empty dictionary. type: dict suboptions: tenant: @@ -134,9 +136,24 @@ destination_port: 25 source_ip_type: custom_source_ip custom_source_address: 11.11.11.2 + associated_epg: + tenant: my_tenant + vrf: my_vrf + ap: my_ap + epg: my_epg state: present delegate_to: localhost +- name: Remove associated EPG from the new Netflow Exporter Policy + cisco.aci.aci_netflow_exporter_policy: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + netflow_exporter_policy: my_netflow_exporter_policy + associated_epg: {} + delegate_to: localhost + - name: Query a Netflow Exporter Policy cisco.aci.aci_netflow_exporter_policy: host: apic @@ -295,14 +312,13 @@ def main(): - new_dscp_spec = dict((k, aci_contract_dscp_spec()[k]) for k in aci_contract_dscp_spec() if k != "aliases") argument_spec = aci_argument_spec() argument_spec.update(aci_annotation_spec()) argument_spec.update(aci_owner_spec()) argument_spec.update( tenant=dict(type="str", aliases=["tenant_name"]), netflow_exporter_policy=dict(type="str", aliases=["netflow_exporter", "netflow_exporter_name", "name"]), - dscp=new_dscp_spec, + dscp=dict((k, aci_contract_dscp_spec()[k]) for k in aci_contract_dscp_spec() if k != "aliases"), destination_address=dict(type="str"), destination_port=dict(type="str"), source_ip_type=dict(type="str", choices=list(MATCH_SOURCE_IP_TYPE_NETFLOW_EXPORTER_MAPPING.keys())), @@ -321,7 +337,7 @@ def main(): ["state", "present", ["tenant", "netflow_exporter_policy", "destination_address", "destination_port"]], ], mutually_exclusive=[ - ("associated_epg", "associated_extepg") + ["associated_epg", "associated_extepg"] ] ) @@ -360,14 +376,14 @@ def main(): aci.get_existing() if state == "present": + child_configs = [] if associated_epg is not None: if all(value is None for value in associated_epg.values()) and isinstance(aci.existing, list) and len(aci.existing) > 0: for child in aci.existing[0].get("netflowExporterPol", {}).get("children", {}): - if child.get("netflowRsExporterToCtx") and child.get("netflowRsExporterToEPg"): - child_configs = [ - dict(netflowRsExporterToCtx=dict(attributes=dict(status="deleted"))), - dict(netflowRsExporterToEPg=dict(attributes=dict(status="deleted"))), - ] + if child.get("netflowRsExporterToCtx"): + child_configs.extend([dict(netflowRsExporterToCtx=dict(attributes=dict(status="deleted")))]) + elif child.get("netflowRsExporterToEPg"): + child_configs.extend([dict(netflowRsExporterToEPg=dict(attributes=dict(status="deleted")))]) elif all(value is not None for value in associated_epg.values()): associated_tenant = associated_epg.get("tenant") child_configs = [ @@ -377,11 +393,10 @@ def main(): elif associated_extepg is not None: if all(value is None for value in associated_extepg.values()) and isinstance(aci.existing, list) and len(aci.existing) > 0: for child in aci.existing[0].get("netflowExporterPol", {}).get("children", {}): - if child.get("netflowRsExporterToCtx") and child.get("netflowRsExporterToEPg"): - child_configs = [ - dict(netflowRsExporterToCtx=dict(attributes=dict(status="deleted"))), - dict(netflowRsExporterToEPg=dict(attributes=dict(status="deleted"))), - ] + if child.get("netflowRsExporterToCtx"): + child_configs.extend([dict(netflowRsExporterToCtx=dict(attributes=dict(status="deleted")))]) + elif child.get("netflowRsExporterToEPg"): + child_configs.extend([dict(netflowRsExporterToEPg=dict(attributes=dict(status="deleted")))]) elif all(value is not None for value in associated_extepg.values()): associated_tenant = associated_extepg.get("tenant") child_configs = [ diff --git a/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml b/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml index d07f1de73..2533ff99b 100644 --- a/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml +++ b/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml @@ -39,6 +39,50 @@ description: Ansible tenant state: present + - name: Add a new VRF + cisco.aci.aci_vrf: + <<: *aci_info + tenant: ansible_tenant + vrf: ansible_vrf + description: ansible VRF for ansible_tenant tenant + state: present + + - name: Add a new Application Profile + cisco.aci.aci_ap: + <<: *aci_info + tenant: ansible_tenant + ap: ansible_ap + description: ansible Application Profile for ansible_tenant tenant + state: present + + - name: Add a new EPG + cisco.aci.aci_epg: + <<: *aci_info + tenant: ansible_tenant + ap: ansible_ap + epg: ansible_epg + description: ansible EPG for ansible_ap Application Profile + state: present + + - name: Add a new L3Out + cisco.aci.aci_l3out: + <<: *aci_info + tenant: ansible_tenant + vrf: ansible_vrf + domain: ansible_dom + l3out: ansible_l3out + description: ansible L3Out for ansible_tenant tenant + state: present + + - name: Add a new External EPG + cisco.aci.aci_l3out_extepg: + <<: *aci_info + tenant: ansible_tenant + extepg: ansible_extepg + l3out: ansible_l3out + description: ansible External EPG for ansible_l3out L3Out + state: present + # CREATE NETFLOW EXPORTER POLICY - name: Add a Netflow Exporter policy (check_mode) cisco.aci.aci_netflow_exporter_policy: &aci_netflow_exporter_policy_present @@ -50,6 +94,11 @@ destination_address: 11.11.11.1 destination_port: smtp source_ip_type: inband_management_ip + associated_epg: + tenant: ansible_tenant + vrf: ansible_vrf + ap: ansible_ap + epg: ansible_epg state: present check_mode: true register: cm_add_netflow_exporter_policy @@ -65,7 +114,7 @@ register: nm_add_netflow_exporter_policy_idempotency - name: Add a second Netflow Exporter policy (normal_mode) - cisco.aci.aci_netflow_exporter_policy: + cisco.aci.aci_netflow_exporter_policy: &aci_netflow_exporter_policy_2_present <<: *aci_info tenant: ansible_tenant netflow_exporter_policy: ansible_netflow_exporter_policy_2 @@ -74,6 +123,11 @@ destination_address: 11.11.11.2 destination_port: https custom_source_address: 12.12.12.2/12 + associated_extepg: + tenant: ansible_tenant + vrf: ansible_vrf + l3out: ansible_l3out + extepg: ansible_extepg state: present register: nm_add_netflow_exporter_policy_2 @@ -90,6 +144,8 @@ - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.attributes.dstPort == "smtp" - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.attributes.sourceIpType == "inband-mgmt-ip" - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.attributes.srcAddr == "0.0.0.0" + - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.children.0.netflowRsExporterToCtx.attributes.tDn == "uni/tn-ansible_tenant/ctx-ansible_vrf" + - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.children.1.netflowRsExporterToEPg.attributes.tDn == "uni/tn-ansible_tenant/ap-ansible_ap/epg-ansible_epg" - nm_add_netflow_exporter_policy_idempotency is not changed - nm_add_netflow_exporter_policy_2 is changed - nm_add_netflow_exporter_policy_2.previous == [] @@ -99,8 +155,10 @@ - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.attributes.dstPort == "https" - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.attributes.sourceIpType == "custom-src-ip" - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.attributes.srcAddr == "12.12.12.2/12" + - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.children.0.netflowRsExporterToCtx.attributes.tDn == "uni/tn-ansible_tenant/ctx-ansible_vrf" + - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.children.1.netflowRsExporterToEPg.attributes.tDn == "uni/tn-ansible_tenant/out-ansible_l3out/instP-ansible_extepg" - # QUERY NETFLOW Exporter POLICY + # QUERY NETFLOW EXPORTER POLICY - name: Query all Netflow Exporter policies cisco.aci.aci_netflow_exporter_policy: <<: *aci_info @@ -125,15 +183,63 @@ - query_ansible_netflow_exporter_policy_1.current.0.netflowExporterPol.attributes.dstPort == "smtp" - query_ansible_netflow_exporter_policy_1.current.0.netflowExporterPol.attributes.sourceIpType == "inband-mgmt-ip" - query_ansible_netflow_exporter_policy_1.current.0.netflowExporterPol.attributes.srcAddr == "0.0.0.0" + - query_ansible_netflow_exporter_policy_1.current.0.netflowExporterPol.children.0.netflowRsExporterToCtx.attributes.tDn == "uni/tn-ansible_tenant/ctx-ansible_vrf" + - query_ansible_netflow_exporter_policy_1.current.0.netflowExporterPol.children.1.netflowRsExporterToEPg.attributes.tDn == "uni/tn-ansible_tenant/ap-ansible_ap/epg-ansible_epg" + + #REMOVE ASSOCIATED EPG/EXTERNAL EPG FROM NETFLOW EXPORTER POLICIES + - name: Remove associated EPG from first Netflow Exporter policy (check_mode) + cisco.aci.aci_netflow_exporter_policy: &aci_netflow_exporter_policy_remove_epg + <<: *aci_netflow_exporter_policy_present + associated_epg: {} + check_mode: true + register: cm_remove_epg_netflow_exporter_policy + + - name: Remove associated EPG from first Netflow Exporter policy (normal_mode) + cisco.aci.aci_netflow_exporter_policy: + <<: *aci_netflow_exporter_policy_remove_epg + register: nm_remove_epg_netflow_exporter_policy + + - name: Remove associated EPG from first Netflow Exporter policy again - testing idempotency + cisco.aci.aci_netflow_exporter_policy: + <<: *aci_netflow_exporter_policy_remove_epg + register: nm_remove_epg_netflow_exporter_policy_idempotency + + - name: Remove associated external EPG from second Netflow Exporter policy (normal_mode) + cisco.aci.aci_netflow_exporter_policy: &aci_netflow_exporter_policy_2_remove_extepg + <<: *aci_netflow_exporter_policy_2_present + associated_extepg: {} + register: nm_remove_extepg_netflow_exporter_policy_2 + + - name: Remove associated external EPG from second Netflow Exporter policy again - testing idempotency + cisco.aci.aci_netflow_exporter_policy: + <<: *aci_netflow_exporter_policy_2_remove_extepg + register: nm_remove_extepg_netflow_exporter_policy_2_idempotency + + - name: Asserts for associated EPGs removal tasks + ansible.builtin.assert: + that: + - cm_remove_epg_netflow_exporter_policy is changed + - cm_remove_epg_netflow_exporter_policy.current == cm_remove_epg_netflow_exporter_policy.previous + - nm_remove_epg_netflow_exporter_policy is changed + - nm_remove_epg_netflow_exporter_policy.proposed.netflowExporterPol.children.0.netflowRsExporterToCtx.attributes.status == "deleted" + - nm_remove_epg_netflow_exporter_policy.proposed.netflowExporterPol.children.1.netflowRsExporterToEPg.attributes.status == "deleted" + - '"children" not in nm_remove_epg_netflow_exporter_policy.current.0.netflowExporterPol' + - nm_remove_epg_netflow_exporter_policy_idempotency is not changed + - nm_remove_extepg_netflow_exporter_policy_2 is changed + - nm_remove_extepg_netflow_exporter_policy_2.proposed.netflowExporterPol.children.0.netflowRsExporterToCtx.attributes.status == "deleted" + - nm_remove_extepg_netflow_exporter_policy_2.proposed.netflowExporterPol.children.1.netflowRsExporterToEPg.attributes.status == "deleted" + - '"children" not in nm_remove_extepg_netflow_exporter_policy_2.current.0.netflowExporterPol' + - nm_remove_extepg_netflow_exporter_policy_2_idempotency is not changed #UPDATE NETFLOW EXPORTER POLICY - name: Update first Netflow Exporter policy (check_mode) cisco.aci.aci_netflow_exporter_policy: &aci_netflow_exporter_policy_update - <<: *aci_netflow_exporter_policy_present + <<: *aci_netflow_exporter_policy_remove_epg dscp: AF13 destination_address: 11.11.11.3 destination_port: http source_ip_type: out_of_band_management_ip + description: Updated description for first ansible Netflow Exporter policy check_mode: true register: cm_update_netflow_exporter_policy @@ -158,6 +264,7 @@ - nm_update_netflow_exporter_policy.current.0.netflowExporterPol.attributes.dstPort == "http" - nm_update_netflow_exporter_policy.current.0.netflowExporterPol.attributes.sourceIpType == "oob-mgmt-ip" - nm_update_netflow_exporter_policy.current.0.netflowExporterPol.attributes.srcAddr == "0.0.0.0" + - nm_update_netflow_exporter_policy.current.0.netflowExporterPol.attributes.descr == "Updated description for first ansible Netflow Exporter policy" - nm_udpate_netflow_exporter_policy_idempotency is not changed # DELETE NETFLOW EXPORTER POLICY @@ -185,6 +292,7 @@ - cm_remove_netflow_exporter_policy.proposed == {} - nm_remove_netflow_exporter_policy is changed - nm_remove_netflow_exporter_policy.previous != [] + - nm_remove_netflow_exporter_policy.proposed == {} - nm_remove_netflow_exporter_policy.current == [] - nm_remove_netflow_exporter_policy_idempotency is not changed - nm_remove_netflow_exporter_policy_idempotency.previous == [] From c126e9cbf5c1f3f228243d8720a6a65830b596fb Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Thu, 18 Jan 2024 13:40:49 -0500 Subject: [PATCH 08/15] [ignore] Modiy module and test file for aci_netflow_monitor_policy. --- plugins/modules/aci_netflow_monitor_policy.py | 1 + .../aci_netflow_monitor_policy/tasks/main.yml | 33 ++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/plugins/modules/aci_netflow_monitor_policy.py b/plugins/modules/aci_netflow_monitor_policy.py index 14164040b..13d82a9ca 100644 --- a/plugins/modules/aci_netflow_monitor_policy.py +++ b/plugins/modules/aci_netflow_monitor_policy.py @@ -30,6 +30,7 @@ netflow_record_policy: description: - The name of the Netflow Record Policy. + - To remove the current Netflow Record Policy, pass an empty string. type: str aliases: [ netflow_record, netflow_record_name ] description: diff --git a/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml b/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml index 7149221f3..ad82c9543 100644 --- a/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml +++ b/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml @@ -114,10 +114,40 @@ - query_ansible_netflow_monitor_policy_1.current.0.netflowMonitorPol.attributes.name == "ansible_netflow_monitor_policy_1" - query_ansible_netflow_monitor_policy_1.current.0.netflowMonitorPol.children.0.netflowRsMonitorToRecord.attributes.tDn == "uni/tn-ansible_tenant/recordpol-ansible_netflow_record_policy" + # UPDATE NETFLOW MONITOR POLICY + - name: Add a Netflow Monitor policy (check_mode) + cisco.aci.aci_netflow_monitor_policy: &aci_netflow_monitor_policy_update + <<: *aci_netflow_monitor_policy_present + netflow_record_policy: "" + description: Updated Netflow Monitor policy 1 for ansible_tenant tenant + state: present + check_mode: true + register: cm_update_netflow_monitor_policy + + - name: Add a Netflow Monitor policy (normal_mode) + cisco.aci.aci_netflow_monitor_policy: + <<: *aci_netflow_monitor_policy_update + register: nm_update_netflow_monitor_policy + + - name: Add the first Netflow Monitor policy again - testing idempotency + cisco.aci.aci_netflow_monitor_policy: + <<: *aci_netflow_monitor_policy_update + register: nm_update_netflow_monitor_policy_idempotency + + - name: Asserts for Netflow Monitor policy update tasks + ansible.builtin.assert: + that: + - cm_update_netflow_monitor_policy is changed + - cm_update_netflow_monitor_policy.previous == cm_update_netflow_monitor_policy.current + - nm_update_netflow_monitor_policy is changed + - nm_update_netflow_monitor_policy.current.0.netflowMonitorPol.attributes.descr == "Updated Netflow Monitor policy 1 for ansible_tenant tenant" + - nm_update_netflow_monitor_policy.current.0.netflowMonitorPol.children.0.netflowRsMonitorToRecord.attributes.tnNetflowRecordPolName == "" + - nm_update_netflow_monitor_policy_idempotency is not changed + # DELETE NETFLOW MONITOR POLICY - name: Remove Netflow Monitor policy (check_mode) cisco.aci.aci_netflow_monitor_policy: &netflow_monitor_policy_absent - <<: *aci_netflow_monitor_policy_present + <<: *aci_netflow_monitor_policy_update state: absent check_mode: true register: cm_remove_netflow_monitor_policy @@ -139,6 +169,7 @@ - cm_remove_netflow_monitor_policy.proposed == {} - nm_remove_netflow_monitor_policy is changed - nm_remove_netflow_monitor_policy.previous != [] + - nm_remove_netflow_monitor_policy.proposed == {} - nm_remove_netflow_monitor_policy.current == [] - nm_remove_netflow_monitor_policy_idempotency is not changed - nm_remove_netflow_monitor_policy_idempotency.previous == [] From 26e8c70efd06ecc0ef7da9c21b742bfa6d87d372 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Thu, 18 Jan 2024 13:46:07 -0500 Subject: [PATCH 09/15] [ignore] Apply Black on aci_netflow_exporter_policy. --- .../modules/aci_netflow_exporter_policy.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/plugins/modules/aci_netflow_exporter_policy.py b/plugins/modules/aci_netflow_exporter_policy.py index 64d000d73..61d2ccb44 100644 --- a/plugins/modules/aci_netflow_exporter_policy.py +++ b/plugins/modules/aci_netflow_exporter_policy.py @@ -336,9 +336,7 @@ def main(): ["state", "absent", ["tenant", "netflow_exporter_policy"]], ["state", "present", ["tenant", "netflow_exporter_policy", "destination_address", "destination_port"]], ], - mutually_exclusive=[ - ["associated_epg", "associated_extepg"] - ] + mutually_exclusive=[["associated_epg", "associated_extepg"]] ) tenant = module.params.get("tenant") @@ -388,9 +386,13 @@ def main(): associated_tenant = associated_epg.get("tenant") child_configs = [ dict(netflowRsExporterToCtx=dict(attributes=dict(tDn="uni/tn-{0}/ctx-{1}".format(associated_tenant, associated_epg.get("vrf"))))), - dict(netflowRsExporterToEPg=dict(attributes=dict(tDn="uni/tn-{0}/ap-{1}/epg-{2}".format(associated_tenant, associated_epg.get("ap"), associated_epg.get("epg"))))), + dict( + netflowRsExporterToEPg=dict( + attributes=dict(tDn="uni/tn-{0}/ap-{1}/epg-{2}".format(associated_tenant, associated_epg.get("ap"), associated_epg.get("epg"))) + ) + ), ] - elif associated_extepg is not None: + elif associated_extepg is not None: if all(value is None for value in associated_extepg.values()) and isinstance(aci.existing, list) and len(aci.existing) > 0: for child in aci.existing[0].get("netflowExporterPol", {}).get("children", {}): if child.get("netflowRsExporterToCtx"): @@ -401,7 +403,13 @@ def main(): associated_tenant = associated_extepg.get("tenant") child_configs = [ dict(netflowRsExporterToCtx=dict(attributes=dict(tDn="uni/tn-{0}/ctx-{1}".format(associated_tenant, associated_extepg.get("vrf"))))), - dict(netflowRsExporterToEPg=dict(attributes=dict(tDn="uni/tn-{0}/out-{1}/instP-{2}".format(associated_tenant, associated_extepg.get("l3out"), associated_extepg.get("extepg"))))), + dict( + netflowRsExporterToEPg=dict( + attributes=dict( + tDn="uni/tn-{0}/out-{1}/instP-{2}".format(associated_tenant, associated_extepg.get("l3out"), associated_extepg.get("extepg")) + ) + ) + ), ] aci.payload( From ea10f8dfc0770715f453af1f0da3c2b634c422dc Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Thu, 18 Jan 2024 14:24:04 -0500 Subject: [PATCH 10/15] [ignore] Modify aci_netflow_exporter_policy.py and aci.py to pass Sanity tests. --- plugins/module_utils/aci.py | 3 +++ plugins/modules/aci_netflow_exporter_policy.py | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index b0959ad23..6e9d0a8c1 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -336,6 +336,7 @@ def action_rule_set_dampening_spec(): suppress=dict(type="int"), ) + def associated_netflow_exporter_epg_spec(): return dict( tenant=dict(type="str"), @@ -344,6 +345,7 @@ def associated_netflow_exporter_epg_spec(): epg=dict(type="str"), ) + def associated_netflow_exporter_extepg_spec(): return dict( tenant=dict(type="str"), @@ -352,6 +354,7 @@ def associated_netflow_exporter_extepg_spec(): extepg=dict(type="str"), ) + class ACIModule(object): def __init__(self, module): self.module = module diff --git a/plugins/modules/aci_netflow_exporter_policy.py b/plugins/modules/aci_netflow_exporter_policy.py index 61d2ccb44..43a76b484 100644 --- a/plugins/modules/aci_netflow_exporter_policy.py +++ b/plugins/modules/aci_netflow_exporter_policy.py @@ -69,12 +69,15 @@ vrf: description: - The name of the associated VRF. + type: str ap: description: - The name of the associated Application Profile to which the associated EPG belongs. + type: str epg: description: - The name of the associated EPG. + type: str associated_extepg: description: - The associated external EPG. @@ -88,12 +91,15 @@ vrf: description: - The name of the associated VRF. + type: str l3out: description: - The name of the L3Out to which the associated external EPG belongs. + type: str extepg: description: - The name of the associated EPG. + type: str description: description: - The description for the Netflow Exporter Policy. @@ -336,7 +342,7 @@ def main(): ["state", "absent", ["tenant", "netflow_exporter_policy"]], ["state", "present", ["tenant", "netflow_exporter_policy", "destination_address", "destination_port"]], ], - mutually_exclusive=[["associated_epg", "associated_extepg"]] + mutually_exclusive=[["associated_epg", "associated_extepg"]], ) tenant = module.params.get("tenant") From 1c5c2abbe9ece600832aaa72304fa75bd30747af Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Mon, 22 Jan 2024 13:16:50 -0500 Subject: [PATCH 11/15] [ignore] Add aliases for associated EPG in aci_netflow_exporter_policy and Add more assertions for all test files. --- plugins/modules/aci_netflow_exporter_policy.py | 16 +++++++++------- .../aci_netflow_exporter_policy/tasks/main.yml | 18 ++++++++++++++++++ .../aci_netflow_monitor_policy/tasks/main.yml | 7 +++++++ .../tasks/main.yml | 4 ++++ .../aci_netflow_record_policy/tasks/main.yml | 8 ++++++++ 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/plugins/modules/aci_netflow_exporter_policy.py b/plugins/modules/aci_netflow_exporter_policy.py index 43a76b484..9408ab680 100644 --- a/plugins/modules/aci_netflow_exporter_policy.py +++ b/plugins/modules/aci_netflow_exporter_policy.py @@ -48,19 +48,20 @@ source_ip_type: description: - The type of Exporter source IP Address. - - It Can be one of the available management IP Address for a given leaf or a custom IP Address. + - It can be one of the available management IP Address for a given leaf or a custom IP Address. type: str choices: [ custom_source_ip, inband_management_ip, out_of_band_management_ip, ptep ] custom_source_address: description: - - The cutsom source IP address. - - It can only be used if I(source_ip_type) is C(custom_source_ip). + - The custom source IP address. + - It can only be used if O(source_ip_type=custom_source_ip). type: str associated_epg: description: - The associated EPG. - - To Remove the current associated EPG, pass an empty dictionary. + - To remove the current associated EPG, pass an empty dictionary. type: dict + aliases: [ epg ] suboptions: tenant: description: @@ -81,8 +82,9 @@ associated_extepg: description: - The associated external EPG. - - To Remove the current associated external EPG, pass an empty dictionary. + - To remove the current associated external EPG, pass an empty dictionary. type: dict + aliases: [ external_epg, associated_external_epg ] suboptions: tenant: description: @@ -329,8 +331,8 @@ def main(): destination_port=dict(type="str"), source_ip_type=dict(type="str", choices=list(MATCH_SOURCE_IP_TYPE_NETFLOW_EXPORTER_MAPPING.keys())), custom_source_address=dict(type="str"), - associated_epg=dict(type="dict", options=associated_netflow_exporter_epg_spec()), - associated_extepg=dict(type="dict", options=associated_netflow_exporter_extepg_spec()), + associated_epg=dict(type="dict", aliases=["external_epg", "associated_external_epg"], options=associated_netflow_exporter_epg_spec()), + associated_extepg=dict(type="dict", aliases=["epg"], options=associated_netflow_exporter_extepg_spec()), description=dict(type="str", aliases=["descr"]), state=dict(type="str", default="present", choices=["absent", "present", "query"]), ) diff --git a/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml b/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml index 2533ff99b..74e608064 100644 --- a/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml +++ b/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml @@ -137,7 +137,15 @@ - cm_add_netflow_exporter_policy is changed - cm_add_netflow_exporter_policy.previous == [] - cm_add_netflow_exporter_policy.current == [] + - cm_add_netflow_exporter_policy.proposed.netflowExporterPol.attributes.name == "ansible_netflow_exporter_policy_1" + - cm_add_netflow_exporter_policy.proposed.netflowExporterPol.attributes.dscp == "AF12" + - cm_add_netflow_exporter_policy.proposed.netflowExporterPol.attributes.dstAddr == "11.11.11.1" + - cm_add_netflow_exporter_policy.proposed.netflowExporterPol.attributes.dstPort == "smtp" + - cm_add_netflow_exporter_policy.proposed.netflowExporterPol.attributes.sourceIpType == "inband-mgmt-ip" + - cm_add_netflow_exporter_policy.proposed.netflowExporterPol.children.0.netflowRsExporterToCtx.attributes.tDn == "uni/tn-ansible_tenant/ctx-ansible_vrf" + - cm_add_netflow_exporter_policy.proposed.netflowExporterPol.children.1.netflowRsExporterToEPg.attributes.tDn == "uni/tn-ansible_tenant/ap-ansible_ap/epg-ansible_epg" - nm_add_netflow_exporter_policy is changed + - nm_add_netflow_exporter_policy.previous == [] - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.attributes.name == "ansible_netflow_exporter_policy_1" - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.attributes.dscp == "AF12" - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.attributes.dstAddr == "11.11.11.1" @@ -177,6 +185,7 @@ - query_all_netflow_exporter_policy is not changed - query_all_netflow_exporter_policy.current|length >= 2 - query_ansible_netflow_exporter_policy_1 is not changed + - query_ansible_netflow_exporter_policy_1.current|length == 1 - query_ansible_netflow_exporter_policy_1.current.0.netflowExporterPol.attributes.name == "ansible_netflow_exporter_policy_1" - query_ansible_netflow_exporter_policy_1.current.0.netflowExporterPol.attributes.dscp == "AF12" - query_ansible_netflow_exporter_policy_1.current.0.netflowExporterPol.attributes.dstAddr == "11.11.11.1" @@ -220,6 +229,8 @@ that: - cm_remove_epg_netflow_exporter_policy is changed - cm_remove_epg_netflow_exporter_policy.current == cm_remove_epg_netflow_exporter_policy.previous + - cm_remove_epg_netflow_exporter_policy.proposed.netflowExporterPol.children.0.netflowRsExporterToCtx.attributes.status == "deleted" + - cm_remove_epg_netflow_exporter_policy.proposed.netflowExporterPol.children.1.netflowRsExporterToEPg.attributes.status == "deleted" - nm_remove_epg_netflow_exporter_policy is changed - nm_remove_epg_netflow_exporter_policy.proposed.netflowExporterPol.children.0.netflowRsExporterToCtx.attributes.status == "deleted" - nm_remove_epg_netflow_exporter_policy.proposed.netflowExporterPol.children.1.netflowRsExporterToEPg.attributes.status == "deleted" @@ -258,6 +269,12 @@ that: - cm_update_netflow_exporter_policy is changed - cm_update_netflow_exporter_policy.previous == cm_update_netflow_exporter_policy.current + - cm_update_netflow_exporter_policy.proposed.netflowExporterPol.attributes.dscp == "AF13" + - cm_update_netflow_exporter_policy.proposed.netflowExporterPol.attributes.dstAddr == "11.11.11.3" + - cm_update_netflow_exporter_policy.proposed.netflowExporterPol.attributes.dstPort == "http" + - cm_update_netflow_exporter_policy.proposed.netflowExporterPol.attributes.sourceIpType == "oob-mgmt-ip" + - cm_update_netflow_exporter_policy.proposed.netflowExporterPol.attributes.srcAddr == "0.0.0.0" + - cm_update_netflow_exporter_policy.proposed.netflowExporterPol.attributes.descr == "Updated description for first ansible Netflow Exporter policy" - nm_update_netflow_exporter_policy is changed - nm_update_netflow_exporter_policy.current.0.netflowExporterPol.attributes.dscp == "AF13" - nm_update_netflow_exporter_policy.current.0.netflowExporterPol.attributes.dstAddr == "11.11.11.3" @@ -289,6 +306,7 @@ ansible.builtin.assert: that: - cm_remove_netflow_exporter_policy is changed + - cm_remove_netflow_exporter_policy.current == cm_remove_netflow_exporter_policy.previous - cm_remove_netflow_exporter_policy.proposed == {} - nm_remove_netflow_exporter_policy is changed - nm_remove_netflow_exporter_policy.previous != [] diff --git a/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml b/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml index ad82c9543..41917352b 100644 --- a/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml +++ b/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml @@ -84,7 +84,10 @@ - cm_add_netflow_monitor_policy is changed - cm_add_netflow_monitor_policy.previous == [] - cm_add_netflow_monitor_policy.current == [] + - cm_add_netflow_monitor_policy.proposed.netflowMonitorPol.attributes.name == "ansible_netflow_monitor_policy_1" + - cm_add_netflow_monitor_policy.proposed.netflowMonitorPol.children.0.netflowRsMonitorToRecord.attributes.tnNetflowRecordPolName == "ansible_netflow_record_policy" - nm_add_netflow_monitor_policy is changed + - nm_add_netflow_monitor_policy.previous == [] - nm_add_netflow_monitor_policy.current.0.netflowMonitorPol.attributes.name == "ansible_netflow_monitor_policy_1" - nm_add_netflow_monitor_policy.current.0.netflowMonitorPol.children.0.netflowRsMonitorToRecord.attributes.tnNetflowRecordPolName == "ansible_netflow_record_policy" - nm_add_netflow_monitor_policy_idempotency is not changed @@ -111,6 +114,7 @@ - query_all_netflow_monitor_policy is not changed - query_all_netflow_monitor_policy.current|length >= 2 - query_ansible_netflow_monitor_policy_1 is not changed + - query_ansible_netflow_monitor_policy_1.current|length == 1 - query_ansible_netflow_monitor_policy_1.current.0.netflowMonitorPol.attributes.name == "ansible_netflow_monitor_policy_1" - query_ansible_netflow_monitor_policy_1.current.0.netflowMonitorPol.children.0.netflowRsMonitorToRecord.attributes.tDn == "uni/tn-ansible_tenant/recordpol-ansible_netflow_record_policy" @@ -139,6 +143,8 @@ that: - cm_update_netflow_monitor_policy is changed - cm_update_netflow_monitor_policy.previous == cm_update_netflow_monitor_policy.current + - cm_update_netflow_monitor_policy.proposed.netflowMonitorPol.attributes.descr == "Updated Netflow Monitor policy 1 for ansible_tenant tenant" + - cm_update_netflow_monitor_policy.proposed.netflowMonitorPol.children.0.netflowRsMonitorToRecord.attributes.tnNetflowRecordPolName == "" - nm_update_netflow_monitor_policy is changed - nm_update_netflow_monitor_policy.current.0.netflowMonitorPol.attributes.descr == "Updated Netflow Monitor policy 1 for ansible_tenant tenant" - nm_update_netflow_monitor_policy.current.0.netflowMonitorPol.children.0.netflowRsMonitorToRecord.attributes.tnNetflowRecordPolName == "" @@ -166,6 +172,7 @@ ansible.builtin.assert: that: - cm_remove_netflow_monitor_policy is changed + - cm_remove_netflow_monitor_policy.current == cm_remove_netflow_monitor_policy.previous - cm_remove_netflow_monitor_policy.proposed == {} - nm_remove_netflow_monitor_policy is changed - nm_remove_netflow_monitor_policy.previous != [] diff --git a/tests/integration/targets/aci_netflow_monitor_to_exporter/tasks/main.yml b/tests/integration/targets/aci_netflow_monitor_to_exporter/tasks/main.yml index ae695f108..ce0286fa9 100644 --- a/tests/integration/targets/aci_netflow_monitor_to_exporter/tasks/main.yml +++ b/tests/integration/targets/aci_netflow_monitor_to_exporter/tasks/main.yml @@ -103,7 +103,9 @@ - cm_add_netflow_monitor_to_exporter is changed - cm_add_netflow_monitor_to_exporter.previous == [] - cm_add_netflow_monitor_to_exporter.current == [] + - cm_add_netflow_monitor_to_exporter.proposed.netflowRsMonitorToExporter.attributes.tnNetflowExporterPolName == "ansible_netflow_exporter_policy_1" - nm_add_netflow_monitor_to_exporter is changed + - nm_add_netflow_monitor_to_exporter.previous == [] - nm_add_netflow_monitor_to_exporter.current.0.netflowRsMonitorToExporter.attributes.tnNetflowExporterPolName == "ansible_netflow_exporter_policy_1" - nm_add_netflow_monitor_to_exporter_idempotency is not changed - nm_add_netflow_monitor_to_exporter_2 is changed @@ -129,6 +131,7 @@ - query_all_netflow_monitor_to_exporter is not changed - query_all_netflow_monitor_to_exporter.current|length >= 2 - query_ansible_netflow_monitor_policy_to_exporter is not changed + - query_ansible_netflow_monitor_policy_to_exporter.current|length == 1 - query_ansible_netflow_monitor_policy_to_exporter.current.0.netflowRsMonitorToExporter.attributes.tnNetflowExporterPolName == "ansible_netflow_exporter_policy_1" # DELETE NETFLOW MONITOR TO EXPORTER @@ -153,6 +156,7 @@ ansible.builtin.assert: that: - cm_remove_netflow_monitor_to_exporter is changed + - cm_remove_netflow_monitor_to_exporter.current == cm_remove_netflow_monitor_to_exporter.previous - cm_remove_netflow_monitor_to_exporter.proposed == {} - nm_remove_netflow_monitor_to_exporter is changed - nm_remove_netflow_monitor_to_exporter.previous != [] diff --git a/tests/integration/targets/aci_netflow_record_policy/tasks/main.yml b/tests/integration/targets/aci_netflow_record_policy/tasks/main.yml index c98236c7e..e55db9410 100644 --- a/tests/integration/targets/aci_netflow_record_policy/tasks/main.yml +++ b/tests/integration/targets/aci_netflow_record_policy/tasks/main.yml @@ -77,7 +77,11 @@ - cm_add_netflow_record_policy is changed - cm_add_netflow_record_policy.previous == [] - cm_add_netflow_record_policy.current == [] + - cm_add_netflow_record_policy.proposed.netflowRecordPol.attributes.name == "ansible_netflow_record_policy_1" + - cm_add_netflow_record_policy.proposed.netflowRecordPol.attributes.collect == "count-bytes,sampler-id" + - cm_add_netflow_record_policy.proposed.netflowRecordPol.attributes.match == "dst-ip,src-ip" - nm_add_netflow_record_policy is changed + - nm_add_netflow_record_policy.previous == [] - nm_add_netflow_record_policy.current.0.netflowRecordPol.attributes.name == "ansible_netflow_record_policy_1" - nm_add_netflow_record_policy.current.0.netflowRecordPol.attributes.collect == "count-bytes,sampler-id" - nm_add_netflow_record_policy.current.0.netflowRecordPol.attributes.match == "dst-ip,src-ip" @@ -107,6 +111,7 @@ - query_all_netflow_record_policy is not changed - query_all_netflow_record_policy.current|length >= 2 - query_ansible_netflow_record_policy_1 is not changed + - query_ansible_netflow_record_policy_1.current|length == 1 - query_ansible_netflow_record_policy_1.current.0.netflowRecordPol.attributes.name == "ansible_netflow_record_policy_1" - query_ansible_netflow_record_policy_1.current.0.netflowRecordPol.attributes.collect == "count-bytes,sampler-id" - query_ansible_netflow_record_policy_1.current.0.netflowRecordPol.attributes.match == "dst-ip,src-ip" @@ -136,6 +141,8 @@ that: - cm_update_netflow_record_policy is changed - cm_update_netflow_record_policy.previous == cm_update_netflow_record_policy.current + - cm_update_netflow_record_policy.proposed.netflowRecordPol.attributes.name == "ansible_netflow_record_policy_1" + - cm_update_netflow_record_policy.proposed.netflowRecordPol.attributes.collect == "count-pkts,pkt-disp" - nm_update_netflow_record_policy is changed - nm_update_netflow_record_policy.current.0.netflowRecordPol.attributes.name == "ansible_netflow_record_policy_1" - nm_update_netflow_record_policy.current.0.netflowRecordPol.attributes.collect == "count-pkts,pkt-disp" @@ -164,6 +171,7 @@ ansible.builtin.assert: that: - cm_remove_netflow_record_policy is changed + - cm_remove_netflow_record_policy.current == cm_remove_netflow_record_policy.previous - cm_remove_netflow_record_policy.proposed == {} - nm_remove_netflow_record_policy is changed - nm_remove_netflow_record_policy.previous != [] From 5a80ac178237424e86df872fc75b652e637e84c2 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Tue, 23 Jan 2024 13:21:38 -0500 Subject: [PATCH 12/15] [ignore] Add associated_vrf attribute for aci_netflow_exporter_policy and modify all test files. --- plugins/module_utils/aci.py | 9 ++- .../modules/aci_netflow_exporter_policy.py | 68 +++++++++++-------- .../tasks/main.yml | 29 ++++++-- .../aci_netflow_monitor_policy/tasks/main.yml | 5 ++ .../tasks/main.yml | 1 + .../aci_netflow_record_policy/tasks/main.yml | 8 +++ 6 files changed, 82 insertions(+), 38 deletions(-) diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 6e9d0a8c1..2de3e570c 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -340,7 +340,6 @@ def action_rule_set_dampening_spec(): def associated_netflow_exporter_epg_spec(): return dict( tenant=dict(type="str"), - vrf=dict(type="str"), ap=dict(type="str"), epg=dict(type="str"), ) @@ -349,12 +348,18 @@ def associated_netflow_exporter_epg_spec(): def associated_netflow_exporter_extepg_spec(): return dict( tenant=dict(type="str"), - vrf=dict(type="str"), l3out=dict(type="str"), extepg=dict(type="str"), ) +def associated_netflow_exporter_vrf_spec(): + return dict( + tenant=dict(type="str"), + vrf=dict(type="str"), + ) + + class ACIModule(object): def __init__(self, module): self.module = module diff --git a/plugins/modules/aci_netflow_exporter_policy.py b/plugins/modules/aci_netflow_exporter_policy.py index 9408ab680..1b8be4e7b 100644 --- a/plugins/modules/aci_netflow_exporter_policy.py +++ b/plugins/modules/aci_netflow_exporter_policy.py @@ -65,11 +65,7 @@ suboptions: tenant: description: - - The name of the tenant to which the associated AP/EPG and VRF belong. - type: str - vrf: - description: - - The name of the associated VRF. + - The name of the tenant to which the associated AP/EPG belong. type: str ap: description: @@ -88,11 +84,7 @@ suboptions: tenant: description: - - The name of the tenant to which the associated L3Out/external EPG and VRF belong. - type: str - vrf: - description: - - The name of the associated VRF. + - The name of the tenant to which the associated L3Out/external EPG belong. type: str l3out: description: @@ -102,6 +94,21 @@ description: - The name of the associated EPG. type: str + associated_vrf: + description: + - The associated VRF. + - To remove the current associated VRF, pass an empty dictionary. + type: dict + aliases: [ vrf, context, associated_context ] + suboptions: + tenant: + description: + - The name of the tenant to which the associated VRF belongs. + type: str + vrf: + description: + - The name of the associated VRF. + type: str description: description: - The description for the Netflow Exporter Policy. @@ -315,6 +322,7 @@ aci_contract_dscp_spec, associated_netflow_exporter_epg_spec, associated_netflow_exporter_extepg_spec, + associated_netflow_exporter_vrf_spec, ) from ansible_collections.cisco.aci.plugins.module_utils.constants import MATCH_SOURCE_IP_TYPE_NETFLOW_EXPORTER_MAPPING @@ -331,8 +339,9 @@ def main(): destination_port=dict(type="str"), source_ip_type=dict(type="str", choices=list(MATCH_SOURCE_IP_TYPE_NETFLOW_EXPORTER_MAPPING.keys())), custom_source_address=dict(type="str"), - associated_epg=dict(type="dict", aliases=["external_epg", "associated_external_epg"], options=associated_netflow_exporter_epg_spec()), - associated_extepg=dict(type="dict", aliases=["epg"], options=associated_netflow_exporter_extepg_spec()), + associated_epg=dict(type="dict", aliases=["epg"], options=associated_netflow_exporter_epg_spec()), + associated_extepg=dict(type="dict", aliases=["external_epg", "associated_external_epg"], options=associated_netflow_exporter_extepg_spec()), + associated_vrf=dict(type="dict", aliases=["vrf", "associated_context", "context"], options=associated_netflow_exporter_vrf_spec()), description=dict(type="str", aliases=["descr"]), state=dict(type="str", default="present", choices=["absent", "present", "query"]), ) @@ -357,6 +366,7 @@ def main(): custom_source_address = module.params.get("custom_source_address") associated_epg = module.params.get("associated_epg") associated_extepg = module.params.get("associated_extepg") + associated_vrf = module.params.get("associated_vrf") state = module.params.get("state") aci = ACIModule(module) @@ -383,43 +393,43 @@ def main(): if state == "present": child_configs = [] - if associated_epg is not None: - if all(value is None for value in associated_epg.values()) and isinstance(aci.existing, list) and len(aci.existing) > 0: + if associated_vrf is not None: + if all(value is None for value in associated_vrf.values()) and isinstance(aci.existing, list) and len(aci.existing) > 0: for child in aci.existing[0].get("netflowExporterPol", {}).get("children", {}): if child.get("netflowRsExporterToCtx"): child_configs.extend([dict(netflowRsExporterToCtx=dict(attributes=dict(status="deleted")))]) - elif child.get("netflowRsExporterToEPg"): + elif all(value is not None for value in associated_vrf.values()): + child_configs.extend([ + dict(netflowRsExporterToCtx=dict(attributes=dict(tDn="uni/tn-{0}/ctx-{1}".format(associated_vrf.get("tenant"), associated_vrf.get("vrf"))))), + ]) + if associated_epg is not None: + if all(value is None for value in associated_epg.values()) and isinstance(aci.existing, list) and len(aci.existing) > 0: + for child in aci.existing[0].get("netflowExporterPol", {}).get("children", {}): + if child.get("netflowRsExporterToEPg"): child_configs.extend([dict(netflowRsExporterToEPg=dict(attributes=dict(status="deleted")))]) elif all(value is not None for value in associated_epg.values()): - associated_tenant = associated_epg.get("tenant") - child_configs = [ - dict(netflowRsExporterToCtx=dict(attributes=dict(tDn="uni/tn-{0}/ctx-{1}".format(associated_tenant, associated_epg.get("vrf"))))), + child_configs.extend([ dict( netflowRsExporterToEPg=dict( - attributes=dict(tDn="uni/tn-{0}/ap-{1}/epg-{2}".format(associated_tenant, associated_epg.get("ap"), associated_epg.get("epg"))) + attributes=dict(tDn="uni/tn-{0}/ap-{1}/epg-{2}".format(associated_epg.get("tenant"), associated_epg.get("ap"), associated_epg.get("epg"))) ) ), - ] + ]) elif associated_extepg is not None: if all(value is None for value in associated_extepg.values()) and isinstance(aci.existing, list) and len(aci.existing) > 0: for child in aci.existing[0].get("netflowExporterPol", {}).get("children", {}): - if child.get("netflowRsExporterToCtx"): - child_configs.extend([dict(netflowRsExporterToCtx=dict(attributes=dict(status="deleted")))]) - elif child.get("netflowRsExporterToEPg"): + if child.get("netflowRsExporterToEPg"): child_configs.extend([dict(netflowRsExporterToEPg=dict(attributes=dict(status="deleted")))]) elif all(value is not None for value in associated_extepg.values()): - associated_tenant = associated_extepg.get("tenant") - child_configs = [ - dict(netflowRsExporterToCtx=dict(attributes=dict(tDn="uni/tn-{0}/ctx-{1}".format(associated_tenant, associated_extepg.get("vrf"))))), + child_configs.extend([ dict( netflowRsExporterToEPg=dict( attributes=dict( - tDn="uni/tn-{0}/out-{1}/instP-{2}".format(associated_tenant, associated_extepg.get("l3out"), associated_extepg.get("extepg")) + tDn="uni/tn-{0}/out-{1}/instP-{2}".format(associated_extepg.get("tenant"), associated_extepg.get("l3out"), associated_extepg.get("extepg")) ) ) ), - ] - + ]) aci.payload( aci_class="netflowExporterPol", class_config=dict( diff --git a/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml b/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml index 74e608064..a5b58cb39 100644 --- a/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml +++ b/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml @@ -96,10 +96,12 @@ source_ip_type: inband_management_ip associated_epg: tenant: ansible_tenant - vrf: ansible_vrf ap: ansible_ap epg: ansible_epg state: present + associated_vrf: + tenant: ansible_tenant + vrf: ansible_vrf check_mode: true register: cm_add_netflow_exporter_policy @@ -125,7 +127,6 @@ custom_source_address: 12.12.12.2/12 associated_extepg: tenant: ansible_tenant - vrf: ansible_vrf l3out: ansible_l3out extepg: ansible_extepg state: present @@ -155,6 +156,14 @@ - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.children.0.netflowRsExporterToCtx.attributes.tDn == "uni/tn-ansible_tenant/ctx-ansible_vrf" - nm_add_netflow_exporter_policy.current.0.netflowExporterPol.children.1.netflowRsExporterToEPg.attributes.tDn == "uni/tn-ansible_tenant/ap-ansible_ap/epg-ansible_epg" - nm_add_netflow_exporter_policy_idempotency is not changed + - nm_add_netflow_exporter_policy_idempotency.current.0.netflowExporterPol.attributes.name == "ansible_netflow_exporter_policy_1" + - nm_add_netflow_exporter_policy_idempotency.current.0.netflowExporterPol.attributes.dscp == "AF12" + - nm_add_netflow_exporter_policy_idempotency.current.0.netflowExporterPol.attributes.dstAddr == "11.11.11.1" + - nm_add_netflow_exporter_policy_idempotency.current.0.netflowExporterPol.attributes.dstPort == "smtp" + - nm_add_netflow_exporter_policy_idempotency.current.0.netflowExporterPol.attributes.sourceIpType == "inband-mgmt-ip" + - nm_add_netflow_exporter_policy_idempotency.current.0.netflowExporterPol.attributes.srcAddr == "0.0.0.0" + - nm_add_netflow_exporter_policy_idempotency.current.0.netflowExporterPol.children.0.netflowRsExporterToCtx.attributes.tDn == "uni/tn-ansible_tenant/ctx-ansible_vrf" + - nm_add_netflow_exporter_policy_idempotency.current.0.netflowExporterPol.children.1.netflowRsExporterToEPg.attributes.tDn == "uni/tn-ansible_tenant/ap-ansible_ap/epg-ansible_epg" - nm_add_netflow_exporter_policy_2 is changed - nm_add_netflow_exporter_policy_2.previous == [] - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.attributes.name == "ansible_netflow_exporter_policy_2" @@ -163,8 +172,7 @@ - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.attributes.dstPort == "https" - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.attributes.sourceIpType == "custom-src-ip" - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.attributes.srcAddr == "12.12.12.2/12" - - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.children.0.netflowRsExporterToCtx.attributes.tDn == "uni/tn-ansible_tenant/ctx-ansible_vrf" - - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.children.1.netflowRsExporterToEPg.attributes.tDn == "uni/tn-ansible_tenant/out-ansible_l3out/instP-ansible_extepg" + - nm_add_netflow_exporter_policy_2.current.0.netflowExporterPol.children.0.netflowRsExporterToEPg.attributes.tDn == "uni/tn-ansible_tenant/out-ansible_l3out/instP-ansible_extepg" # QUERY NETFLOW EXPORTER POLICY - name: Query all Netflow Exporter policies @@ -200,6 +208,7 @@ cisco.aci.aci_netflow_exporter_policy: &aci_netflow_exporter_policy_remove_epg <<: *aci_netflow_exporter_policy_present associated_epg: {} + associated_vrf: {} check_mode: true register: cm_remove_epg_netflow_exporter_policy @@ -236,11 +245,12 @@ - nm_remove_epg_netflow_exporter_policy.proposed.netflowExporterPol.children.1.netflowRsExporterToEPg.attributes.status == "deleted" - '"children" not in nm_remove_epg_netflow_exporter_policy.current.0.netflowExporterPol' - nm_remove_epg_netflow_exporter_policy_idempotency is not changed + - '"children" not in nm_remove_epg_netflow_exporter_policy_idempotency.current.0.netflowExporterPol' - nm_remove_extepg_netflow_exporter_policy_2 is changed - - nm_remove_extepg_netflow_exporter_policy_2.proposed.netflowExporterPol.children.0.netflowRsExporterToCtx.attributes.status == "deleted" - - nm_remove_extepg_netflow_exporter_policy_2.proposed.netflowExporterPol.children.1.netflowRsExporterToEPg.attributes.status == "deleted" + - nm_remove_extepg_netflow_exporter_policy_2.proposed.netflowExporterPol.children.0.netflowRsExporterToEPg.attributes.status == "deleted" - '"children" not in nm_remove_extepg_netflow_exporter_policy_2.current.0.netflowExporterPol' - nm_remove_extepg_netflow_exporter_policy_2_idempotency is not changed + - '"children" not in nm_remove_extepg_netflow_exporter_policy_2_idempotency.current.0.netflowExporterPol' #UPDATE NETFLOW EXPORTER POLICY - name: Update first Netflow Exporter policy (check_mode) @@ -273,7 +283,6 @@ - cm_update_netflow_exporter_policy.proposed.netflowExporterPol.attributes.dstAddr == "11.11.11.3" - cm_update_netflow_exporter_policy.proposed.netflowExporterPol.attributes.dstPort == "http" - cm_update_netflow_exporter_policy.proposed.netflowExporterPol.attributes.sourceIpType == "oob-mgmt-ip" - - cm_update_netflow_exporter_policy.proposed.netflowExporterPol.attributes.srcAddr == "0.0.0.0" - cm_update_netflow_exporter_policy.proposed.netflowExporterPol.attributes.descr == "Updated description for first ansible Netflow Exporter policy" - nm_update_netflow_exporter_policy is changed - nm_update_netflow_exporter_policy.current.0.netflowExporterPol.attributes.dscp == "AF13" @@ -283,6 +292,11 @@ - nm_update_netflow_exporter_policy.current.0.netflowExporterPol.attributes.srcAddr == "0.0.0.0" - nm_update_netflow_exporter_policy.current.0.netflowExporterPol.attributes.descr == "Updated description for first ansible Netflow Exporter policy" - nm_udpate_netflow_exporter_policy_idempotency is not changed + - nm_udpate_netflow_exporter_policy_idempotency.current.0.netflowExporterPol.attributes.dstAddr == "11.11.11.3" + - nm_udpate_netflow_exporter_policy_idempotency.current.0.netflowExporterPol.attributes.dstPort == "http" + - nm_udpate_netflow_exporter_policy_idempotency.current.0.netflowExporterPol.attributes.sourceIpType == "oob-mgmt-ip" + - nm_udpate_netflow_exporter_policy_idempotency.current.0.netflowExporterPol.attributes.srcAddr == "0.0.0.0" + - nm_udpate_netflow_exporter_policy_idempotency.current.0.netflowExporterPol.attributes.descr == "Updated description for first ansible Netflow Exporter policy" # DELETE NETFLOW EXPORTER POLICY - name: Remove Netflow Exporter policy (check_mode) @@ -314,6 +328,7 @@ - nm_remove_netflow_exporter_policy.current == [] - nm_remove_netflow_exporter_policy_idempotency is not changed - nm_remove_netflow_exporter_policy_idempotency.previous == [] + - nm_remove_netflow_exporter_policy_idempotency.current == [] # CLEAN ENVIRONMENT BEFORE ENDING TESTS - name: Remove the ansible_tenant - cleanup before ending tests diff --git a/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml b/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml index 41917352b..ada9d5ad8 100644 --- a/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml +++ b/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml @@ -91,6 +91,8 @@ - nm_add_netflow_monitor_policy.current.0.netflowMonitorPol.attributes.name == "ansible_netflow_monitor_policy_1" - nm_add_netflow_monitor_policy.current.0.netflowMonitorPol.children.0.netflowRsMonitorToRecord.attributes.tnNetflowRecordPolName == "ansible_netflow_record_policy" - nm_add_netflow_monitor_policy_idempotency is not changed + - nm_add_netflow_monitor_policy_idempotency.current.0.netflowMonitorPol.attributes.name == "ansible_netflow_monitor_policy_1" + - nm_add_netflow_monitor_policy_idempotency.current.0.netflowMonitorPol.children.0.netflowRsMonitorToRecord.attributes.tnNetflowRecordPolName == "ansible_netflow_record_policy" - nm_add_netflow_monitor_policy_2 is changed - nm_add_netflow_monitor_policy_2.previous == [] - nm_add_netflow_monitor_policy_2.current.0.netflowMonitorPol.attributes.name == "ansible_netflow_monitor_policy_2" @@ -149,6 +151,8 @@ - nm_update_netflow_monitor_policy.current.0.netflowMonitorPol.attributes.descr == "Updated Netflow Monitor policy 1 for ansible_tenant tenant" - nm_update_netflow_monitor_policy.current.0.netflowMonitorPol.children.0.netflowRsMonitorToRecord.attributes.tnNetflowRecordPolName == "" - nm_update_netflow_monitor_policy_idempotency is not changed + - nm_update_netflow_monitor_policy_idempotency.current.0.netflowMonitorPol.attributes.descr == "Updated Netflow Monitor policy 1 for ansible_tenant tenant" + - nm_update_netflow_monitor_policy_idempotency.current.0.netflowMonitorPol.children.0.netflowRsMonitorToRecord.attributes.tnNetflowRecordPolName == "" # DELETE NETFLOW MONITOR POLICY - name: Remove Netflow Monitor policy (check_mode) @@ -180,6 +184,7 @@ - nm_remove_netflow_monitor_policy.current == [] - nm_remove_netflow_monitor_policy_idempotency is not changed - nm_remove_netflow_monitor_policy_idempotency.previous == [] + - nm_remove_netflow_monitor_policy_idempotency.current == [] # CLEAN ENVIRONMENT BEFORE ENDING TESTS - name: Remove the ansible_tenant - cleanup before ending tests diff --git a/tests/integration/targets/aci_netflow_monitor_to_exporter/tasks/main.yml b/tests/integration/targets/aci_netflow_monitor_to_exporter/tasks/main.yml index ce0286fa9..7ee25f61c 100644 --- a/tests/integration/targets/aci_netflow_monitor_to_exporter/tasks/main.yml +++ b/tests/integration/targets/aci_netflow_monitor_to_exporter/tasks/main.yml @@ -108,6 +108,7 @@ - nm_add_netflow_monitor_to_exporter.previous == [] - nm_add_netflow_monitor_to_exporter.current.0.netflowRsMonitorToExporter.attributes.tnNetflowExporterPolName == "ansible_netflow_exporter_policy_1" - nm_add_netflow_monitor_to_exporter_idempotency is not changed + - nm_add_netflow_monitor_to_exporter_idempotency.current.0.netflowRsMonitorToExporter.attributes.tnNetflowExporterPolName == "ansible_netflow_exporter_policy_1" - nm_add_netflow_monitor_to_exporter_2 is changed - nm_add_netflow_monitor_to_exporter_2.previous == [] - nm_add_netflow_monitor_to_exporter_2.current.0.netflowRsMonitorToExporter.attributes.tnNetflowExporterPolName == "ansible_netflow_exporter_policy_2" diff --git a/tests/integration/targets/aci_netflow_record_policy/tasks/main.yml b/tests/integration/targets/aci_netflow_record_policy/tasks/main.yml index e55db9410..0645e9159 100644 --- a/tests/integration/targets/aci_netflow_record_policy/tasks/main.yml +++ b/tests/integration/targets/aci_netflow_record_policy/tasks/main.yml @@ -86,6 +86,9 @@ - nm_add_netflow_record_policy.current.0.netflowRecordPol.attributes.collect == "count-bytes,sampler-id" - nm_add_netflow_record_policy.current.0.netflowRecordPol.attributes.match == "dst-ip,src-ip" - nm_add_netflow_record_policy_idempotency is not changed + - nm_add_netflow_record_policy_idempotency.current.0.netflowRecordPol.attributes.name == "ansible_netflow_record_policy_1" + - nm_add_netflow_record_policy_idempotency.current.0.netflowRecordPol.attributes.collect == "count-bytes,sampler-id" + - nm_add_netflow_record_policy_idempotency.current.0.netflowRecordPol.attributes.match == "dst-ip,src-ip" - nm_add_netflow_record_policy_2 is changed - nm_add_netflow_record_policy_2.previous == [] - nm_add_netflow_record_policy_2.current.0.netflowRecordPol.attributes.name == "ansible_netflow_record_policy_2" @@ -143,11 +146,15 @@ - cm_update_netflow_record_policy.previous == cm_update_netflow_record_policy.current - cm_update_netflow_record_policy.proposed.netflowRecordPol.attributes.name == "ansible_netflow_record_policy_1" - cm_update_netflow_record_policy.proposed.netflowRecordPol.attributes.collect == "count-pkts,pkt-disp" + - cm_update_netflow_record_policy.proposed.netflowRecordPol.attributes.match == "dst-ipv4,src-ipv4" - nm_update_netflow_record_policy is changed - nm_update_netflow_record_policy.current.0.netflowRecordPol.attributes.name == "ansible_netflow_record_policy_1" - nm_update_netflow_record_policy.current.0.netflowRecordPol.attributes.collect == "count-pkts,pkt-disp" - nm_update_netflow_record_policy.current.0.netflowRecordPol.attributes.match == "dst-ipv4,src-ipv4" - nm_udpate_netflow_record_policy_idempotency is not changed + - nm_udpate_netflow_record_policy_idempotency.current.0.netflowRecordPol.attributes.name == "ansible_netflow_record_policy_1" + - nm_udpate_netflow_record_policy_idempotency.current.0.netflowRecordPol.attributes.collect == "count-pkts,pkt-disp" + - nm_udpate_netflow_record_policy_idempotency.current.0.netflowRecordPol.attributes.match == "dst-ipv4,src-ipv4" # DELETE NETFLOW RECORD POLICY - name: Remove Netflow Record policy (check_mode) @@ -178,6 +185,7 @@ - nm_remove_netflow_record_policy.current == [] - nm_remove_netflow_record_policy_idempotency is not changed - nm_remove_netflow_record_policy_idempotency.previous == [] + - nm_remove_netflow_record_policy_idempotency.current == [] # CLEAN ENVIRONMENT BEFORE ENDING TESTS - name: Remove the ansible_tenant - cleanup before ending tests From 70b8e1e4c42184a7c4b4eda66f9f3026dd9cbbdd Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Tue, 23 Jan 2024 13:49:49 -0500 Subject: [PATCH 13/15] [ignore] Apply Black on aci_netflow_exporter_policy and change the examples. --- .../modules/aci_netflow_exporter_policy.py | 72 +++++++++++++------ 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/plugins/modules/aci_netflow_exporter_policy.py b/plugins/modules/aci_netflow_exporter_policy.py index 1b8be4e7b..27c8f1c6a 100644 --- a/plugins/modules/aci_netflow_exporter_policy.py +++ b/plugins/modules/aci_netflow_exporter_policy.py @@ -129,8 +129,23 @@ notes: - The I(tenant) must exist before using this module in your playbook. The M(cisco.aci.aci_tenant) can be used for this. +- The I(associated_epg) and I(associated_extepg) are mutually exclusive. +- If the I(associated_epg) is used, the I(epg), the(tenant) and + the I(ap) must exist before using this module in your play book. + The M(cisco.aci.aci_epg) and the M(cisco.aci.aci_ap) can be used for this. +- If the I(associated_extepg) is used, the I(extepg), the(tenant) and + the I(l3out) must exist before using this module in your play book. + The M(cisco.aci.aci_l3out_extepg) and the M(cisco.aci.aci_l3out) can be used for this. +- If the I(associated_vrf) is used, the I(vrf) and the I(tenant) must exist + before using this module in your play book. + The M(cisco.aci.aci_vrf) can be used for this. seealso: - module: cisco.aci.aci_tenant +- module: cisco.aci.aci_vrf +- module: cisco.aci.aci_ap +- module: cisco.aci.aci_epg +- module: cisco.aci.aci_l3out +- module: cisco.aci.aci_l3out_extepg - name: APIC Management Information Model reference description: More information about the internal APIC class B(netflow:ExporterPol). link: https://developer.cisco.com/docs/apic-mim-ref/ @@ -153,13 +168,15 @@ custom_source_address: 11.11.11.2 associated_epg: tenant: my_tenant - vrf: my_vrf ap: my_ap epg: my_epg + associated_vrf: + tenant: my_tenant + vrf: my_vrf state: present delegate_to: localhost -- name: Remove associated EPG from the new Netflow Exporter Policy +- name: Remove associated EPG and VRF from the new Netflow Exporter Policy cisco.aci.aci_netflow_exporter_policy: host: apic username: admin @@ -167,6 +184,7 @@ tenant: my_tenant netflow_exporter_policy: my_netflow_exporter_policy associated_epg: {} + associated_vrf: {} delegate_to: localhost - name: Query a Netflow Exporter Policy @@ -399,37 +417,51 @@ def main(): if child.get("netflowRsExporterToCtx"): child_configs.extend([dict(netflowRsExporterToCtx=dict(attributes=dict(status="deleted")))]) elif all(value is not None for value in associated_vrf.values()): - child_configs.extend([ - dict(netflowRsExporterToCtx=dict(attributes=dict(tDn="uni/tn-{0}/ctx-{1}".format(associated_vrf.get("tenant"), associated_vrf.get("vrf"))))), - ]) + child_configs.extend( + [ + dict( + netflowRsExporterToCtx=dict( + attributes=dict(tDn="uni/tn-{0}/ctx-{1}".format(associated_vrf.get("tenant"), associated_vrf.get("vrf"))) + ) + ), + ] + ) if associated_epg is not None: if all(value is None for value in associated_epg.values()) and isinstance(aci.existing, list) and len(aci.existing) > 0: for child in aci.existing[0].get("netflowExporterPol", {}).get("children", {}): if child.get("netflowRsExporterToEPg"): child_configs.extend([dict(netflowRsExporterToEPg=dict(attributes=dict(status="deleted")))]) elif all(value is not None for value in associated_epg.values()): - child_configs.extend([ - dict( - netflowRsExporterToEPg=dict( - attributes=dict(tDn="uni/tn-{0}/ap-{1}/epg-{2}".format(associated_epg.get("tenant"), associated_epg.get("ap"), associated_epg.get("epg"))) - ) - ), - ]) + child_configs.extend( + [ + dict( + netflowRsExporterToEPg=dict( + attributes=dict( + tDn="uni/tn-{0}/ap-{1}/epg-{2}".format(associated_epg.get("tenant"), associated_epg.get("ap"), associated_epg.get("epg")) + ) + ) + ), + ] + ) elif associated_extepg is not None: if all(value is None for value in associated_extepg.values()) and isinstance(aci.existing, list) and len(aci.existing) > 0: for child in aci.existing[0].get("netflowExporterPol", {}).get("children", {}): if child.get("netflowRsExporterToEPg"): child_configs.extend([dict(netflowRsExporterToEPg=dict(attributes=dict(status="deleted")))]) elif all(value is not None for value in associated_extepg.values()): - child_configs.extend([ - dict( - netflowRsExporterToEPg=dict( - attributes=dict( - tDn="uni/tn-{0}/out-{1}/instP-{2}".format(associated_extepg.get("tenant"), associated_extepg.get("l3out"), associated_extepg.get("extepg")) + child_configs.extend( + [ + dict( + netflowRsExporterToEPg=dict( + attributes=dict( + tDn="uni/tn-{0}/out-{1}/instP-{2}".format( + associated_extepg.get("tenant"), associated_extepg.get("l3out"), associated_extepg.get("extepg") + ) + ) ) - ) - ), - ]) + ), + ] + ) aci.payload( aci_class="netflowExporterPol", class_config=dict( From cd56ce7fbd6a838a2588354e6cc4f46a516a2c5f Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Mon, 29 Jan 2024 11:27:13 -0500 Subject: [PATCH 14/15] [ignore] Modify aci_netflow_exporter_policy to pass Sanity test. --- plugins/modules/aci_netflow_exporter_policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/aci_netflow_exporter_policy.py b/plugins/modules/aci_netflow_exporter_policy.py index 27c8f1c6a..e45eb6217 100644 --- a/plugins/modules/aci_netflow_exporter_policy.py +++ b/plugins/modules/aci_netflow_exporter_policy.py @@ -136,7 +136,7 @@ - If the I(associated_extepg) is used, the I(extepg), the(tenant) and the I(l3out) must exist before using this module in your play book. The M(cisco.aci.aci_l3out_extepg) and the M(cisco.aci.aci_l3out) can be used for this. -- If the I(associated_vrf) is used, the I(vrf) and the I(tenant) must exist +- If the I(associated_vrf) is used, the I(vrf) and the I(tenant) must exist before using this module in your play book. The M(cisco.aci.aci_vrf) can be used for this. seealso: From 87194aab9883bf6b06d98e41a7be17b1b5e8e9bd Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Tue, 6 Feb 2024 12:35:42 -0500 Subject: [PATCH 15/15] [ignore] Modify Documentations for aci_netflow_monitor_policy and aci_netflow_monitor_to_exporter. --- plugins/modules/aci_netflow_monitor_policy.py | 3 +++ plugins/modules/aci_netflow_monitor_to_exporter.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/modules/aci_netflow_monitor_policy.py b/plugins/modules/aci_netflow_monitor_policy.py index 13d82a9ca..462187609 100644 --- a/plugins/modules/aci_netflow_monitor_policy.py +++ b/plugins/modules/aci_netflow_monitor_policy.py @@ -53,8 +53,11 @@ notes: - The I(tenant) must exist before using this module in your playbook. The M(cisco.aci.aci_tenant) can be used for this. +- If the I(netflow_record_policy) is used, it must exist before using this module in your playbook. + The M(cisco.aci.aci_netflow_record_policy) can be used for this. seealso: - module: cisco.aci.aci_tenant +- module: cisco.aci.aci_netflow_record_policy - name: APIC Management Information Model reference description: More information about the internal APIC class B(netflow:MonitorPol). link: https://developer.cisco.com/docs/apic-mim-ref/ diff --git a/plugins/modules/aci_netflow_monitor_to_exporter.py b/plugins/modules/aci_netflow_monitor_to_exporter.py index 804c77241..50e2f2524 100644 --- a/plugins/modules/aci_netflow_monitor_to_exporter.py +++ b/plugins/modules/aci_netflow_monitor_to_exporter.py @@ -99,7 +99,7 @@ delegate_to: localhost - name: Delete a Netflow Monitor Policy - cisco.aci.aci_netflow_monitor_policy: + cisco.aci.aci_netflow_monitor_to_exporter: host: apic username: admin password: SomeSecretPassword