diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py index 04f387dd8..2de3e570c 100644 --- a/plugins/module_utils/aci.py +++ b/plugins/module_utils/aci.py @@ -337,6 +337,29 @@ def action_rule_set_dampening_spec(): ) +def associated_netflow_exporter_epg_spec(): + return dict( + tenant=dict(type="str"), + ap=dict(type="str"), + epg=dict(type="str"), + ) + + +def associated_netflow_exporter_extepg_spec(): + return dict( + tenant=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/module_utils/constants.py b/plugins/module_utils/constants.py index 55fe23fff..010ef8860 100644 --- a/plugins/module_utils/constants.py +++ b/plugins/module_utils/constants.py @@ -192,3 +192,39 @@ 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", +) + +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..e45eb6217 --- /dev/null +++ b/plugins/modules/aci_netflow_exporter_policy.py @@ -0,0 +1,490 @@ +#!/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. + 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: + description: + - The remote node destination IP address. + type: str + 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: + 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 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. + type: dict + aliases: [ epg ] + suboptions: + tenant: + description: + - The name of the tenant to which the associated AP/EPG belong. + 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. + - To remove the current associated external EPG, pass an empty dictionary. + type: dict + aliases: [ external_epg, associated_external_epg ] + suboptions: + tenant: + description: + - The name of the tenant to which the associated L3Out/external EPG belong. + 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 + 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. + 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. +- 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/ +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 + associated_epg: + tenant: my_tenant + ap: my_ap + epg: my_epg + associated_vrf: + tenant: my_tenant + vrf: my_vrf + state: present + delegate_to: localhost + +- name: Remove associated EPG and VRF 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: {} + associated_vrf: {} + 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, + 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 + + +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_exporter_policy=dict(type="str", aliases=["netflow_exporter", "netflow_exporter_name", "name"]), + 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())), + custom_source_address=dict(type="str"), + 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"]), + ) + + 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"]], + ], + mutually_exclusive=[["associated_epg", "associated_extepg"]], + ) + + 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") + 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) + + child_classes = ["netflowRsExporterToCtx", "netflowRsExporterToEPg"] + + 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}, + ), + child_classes=child_classes, + ) + + aci.get_existing() + + if state == "present": + child_configs = [] + 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 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()): + 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") + ) + ) + ) + ), + ] + ) + 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, + ), + child_configs=child_configs, + ) + + aci.get_diff(aci_class="netflowExporterPol") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/aci_netflow_monitor_policy.py b/plugins/modules/aci_netflow_monitor_policy.py new file mode 100644 index 000000000..462187609 --- /dev/null +++ b/plugins/modules/aci_netflow_monitor_policy.py @@ -0,0 +1,300 @@ +#!/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. + - To remove the current Netflow Record Policy, pass an empty string. + 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. +- 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/ +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 + netflow_record_policy: my_netflow_record_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_monitor_to_exporter.py b/plugins/modules/aci_netflow_monitor_to_exporter.py new file mode 100644 index 000000000..50e2f2524 --- /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_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: 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/plugins/modules/aci_netflow_record_policy.py b/plugins/modules/aci_netflow_record_policy.py new file mode 100644 index 000000000..e2f04af0c --- /dev/null +++ b/plugins/modules/aci_netflow_record_policy.py @@ -0,0 +1,313 @@ +#!/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 + collect: [pkts_counter, pkt_disposition] + match: [destination_ipv4, source_ipv4] + 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") + + 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) + + 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_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..a5b58cb39 --- /dev/null +++ b/tests/integration/targets/aci_netflow_exporter_policy/tasks/main.yml @@ -0,0 +1,337 @@ +# 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 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 + <<: *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 + associated_epg: + tenant: ansible_tenant + 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 + + - 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_netflow_exporter_policy_2_present + <<: *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 + associated_extepg: + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extepg + 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 == [] + - 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" + - 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_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" + - 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" + - 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 + 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|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" + - 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: {} + associated_vrf: {} + 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 + - 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" + - '"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.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) + cisco.aci.aci_netflow_exporter_policy: &aci_netflow_exporter_policy_update + <<: *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 + + - 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 + - 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.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" + - 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 + - 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) + 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.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 != [] + - 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 == [] + - nm_remove_netflow_exporter_policy_idempotency.current == [] + + # 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_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..ada9d5ad8 --- /dev/null +++ b/tests/integration/targets/aci_netflow_monitor_policy/tasks/main.yml @@ -0,0 +1,193 @@ +# 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 == [] + - 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 + - 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" + + # 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|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" + + # 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 + - 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 == "" + - 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) + cisco.aci.aci_netflow_monitor_policy: &netflow_monitor_policy_absent + <<: *aci_netflow_monitor_policy_update + 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.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 != [] + - 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 == [] + - nm_remove_netflow_monitor_policy_idempotency.current == [] + + # 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..7ee25f61c --- /dev/null +++ b/tests/integration/targets/aci_netflow_monitor_to_exporter/tasks/main.yml @@ -0,0 +1,172 @@ +# 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 == [] + - 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_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" + + # 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|length == 1 + - 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.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 != [] + - 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 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..0645e9159 --- /dev/null +++ b/tests/integration/targets/aci_netflow_record_policy/tasks/main.yml @@ -0,0 +1,194 @@ +# 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 == [] + - 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" + - 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" + - 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|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" + + #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 + - 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) + 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.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 != [] + - 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 + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent