diff --git a/plugins/modules/aci_fabric_node_control.py b/plugins/modules/aci_fabric_node_control.py new file mode 100644 index 000000000..eef1d510b --- /dev/null +++ b/plugins/modules/aci_fabric_node_control.py @@ -0,0 +1,268 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2023, Tim Cragg (@timcragg) +# Copyright: (c) 2023, Akini Ross (@akinross) ' +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( + name=dict(type="str", aliases=["fabric_node_control"]), + description=dict(type="str"), + enable_dom=dict(type="bool"), + feature_selection=dict(type="str", choices=["analytics", "netflow", "telemetry"], aliases=["feature"]), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + aci = ACIModule(module) + + name = module.params.get("name") + description = module.params.get("description") + enable_dom = aci.boolean(module.params.get("enable_dom"), "Dom", "None") + feature_selection = module.params.get("feature_selection") + state = module.params.get("state") + + aci.construct_url( + root_class=dict( + aci_class="fabricNodeControl", + aci_rn="fabric/nodecontrol-{0}".format(name), + module_object=name, + target_filter={"name": name}, + ), + ) + aci.get_existing() + + if state == "present": + aci.payload( + aci_class="fabricNodeControl", + class_config=dict( + name=name, + descr=description, + control=enable_dom, + featureSel=feature_selection, + ), + ) + + aci.get_diff(aci_class="fabricNodeControl") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/aci_fabric_node_control/aliases b/tests/integration/targets/aci_fabric_node_control/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_fabric_node_control/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_fabric_node_control/tasks/main.yml b/tests/integration/targets/aci_fabric_node_control/tasks/main.yml new file mode 100644 index 000000000..63a3c758b --- /dev/null +++ b/tests/integration/targets/aci_fabric_node_control/tasks/main.yml @@ -0,0 +1,179 @@ +# Test code for the ACI modules +# Copyright: (c) 2023, Tim Cragg (@timcragg) +# Copyright: (c) 2023, Akini Ross (@akinross) + +# 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 + 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 + 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 +- name: Remove Fabric Node Control + cisco.aci.aci_fabric_node_control: + <<: *aci_info + name: "{{ item }}" + state: absent + loop: + - ansible_fabric_node_control_1 + - ansible_fabric_node_control_2 + - ansible_fabric_node_control_3 + +# CREATE FABRIC NODE CONTROL +- name: Create Fabric Node Control (check mode) + cisco.aci.aci_fabric_node_control: &aci_fabric_node_control + <<: *aci_info + name: ansible_fabric_node_control_1 + check_mode: true + register: cm_create_fabric_node_control + +- name: Create Fabric Node Control (normal mode) + cisco.aci.aci_fabric_node_control: + <<: *aci_fabric_node_control + register: nm_create_fabric_node_control + +- name: Create Fabric Node Control again + cisco.aci.aci_fabric_node_control: + <<: *aci_fabric_node_control + register: nm_create_fabric_node_control_again + +- name: Verify Fabric Node Control creation + ansible.builtin.assert: + that: + - cm_create_fabric_node_control is changed + - cm_create_fabric_node_control.current == [] + - cm_create_fabric_node_control.previous == [] + - cm_create_fabric_node_control.proposed.fabricNodeControl.attributes.name == "ansible_fabric_node_control_1" + - nm_create_fabric_node_control is changed + - nm_create_fabric_node_control.previous == [] + - nm_create_fabric_node_control.current.0.fabricNodeControl.attributes.control == "" + - nm_create_fabric_node_control.current.0.fabricNodeControl.attributes.descr == "" + - nm_create_fabric_node_control.current.0.fabricNodeControl.attributes.featureSel == "telemetry" + - nm_create_fabric_node_control.current.0.fabricNodeControl.attributes.name == "ansible_fabric_node_control_1" + - nm_create_fabric_node_control_again is not changed + - nm_create_fabric_node_control_again.current.0.fabricNodeControl.attributes.control == "" + - nm_create_fabric_node_control_again.current.0.fabricNodeControl.attributes.descr == "" + - nm_create_fabric_node_control_again.current.0.fabricNodeControl.attributes.featureSel == "telemetry" + - nm_create_fabric_node_control_again.current.0.fabricNodeControl.attributes.name == "ansible_fabric_node_control_1" + - nm_create_fabric_node_control_again.previous.0.fabricNodeControl.attributes.control == "" + - nm_create_fabric_node_control_again.previous.0.fabricNodeControl.attributes.descr == "" + - nm_create_fabric_node_control_again.previous.0.fabricNodeControl.attributes.featureSel == "telemetry" + - nm_create_fabric_node_control_again.previous.0.fabricNodeControl.attributes.name == "ansible_fabric_node_control_1" + +# UPDATE FABRIC NODE CONTROL +- name: Update Fabric Node Control + cisco.aci.aci_fabric_node_control: + <<: *aci_fabric_node_control + description: Updated Ansible Test Fabric Node Control + enable_dom: true + feature_selection: analytics + register: update_fabric_node_control + +- name: Verify Fabric Node Control update + ansible.builtin.assert: + that: + - update_fabric_node_control is changed + - update_fabric_node_control.current.0.fabricNodeControl.attributes.control == "Dom" + - update_fabric_node_control.current.0.fabricNodeControl.attributes.descr == "Updated Ansible Test Fabric Node Control" + - update_fabric_node_control.current.0.fabricNodeControl.attributes.featureSel == "analytics" + - update_fabric_node_control.current.0.fabricNodeControl.attributes.name == "ansible_fabric_node_control_1" + - update_fabric_node_control.previous.0.fabricNodeControl.attributes.control == "" + - update_fabric_node_control.previous.0.fabricNodeControl.attributes.descr == "" + - update_fabric_node_control.previous.0.fabricNodeControl.attributes.featureSel == "telemetry" + - update_fabric_node_control.previous.0.fabricNodeControl.attributes.name == "ansible_fabric_node_control_1" + +# QUERY FABRIC NODE CONTROL +- name: Create another Fabric Node Control + cisco.aci.aci_fabric_node_control: + <<: *aci_info + name: "{{ item }}" + feature_selection: netflow + loop: + - ansible_fabric_node_control_2 + - ansible_fabric_node_control_3 + +- name: Query a Fabric Node Control + cisco.aci.aci_fabric_node_control: + <<: *aci_fabric_node_control + state: query + register: query_one + +- name: Query all Fabric Node Controls + cisco.aci.aci_fabric_node_control: + <<: *aci_info + state: query + register: query_all + +- name: Verify Fabric Node Control queries + ansible.builtin.assert: + that: + - query_one is not changed + - query_one.current | length == 1 + - query_one.current.0.fabricNodeControl.attributes.control == "Dom" + - query_one.current.0.fabricNodeControl.attributes.descr == "Updated Ansible Test Fabric Node Control" + - query_one.current.0.fabricNodeControl.attributes.featureSel == "analytics" + - query_one.current.0.fabricNodeControl.attributes.name == "ansible_fabric_node_control_1" + - query_all is not changed + - query_all.current | length > 3 + +# REMOVE FABRIC NODE CONTROL +- name: Remove Fabric Node Control (check mode) + cisco.aci.aci_fabric_node_control: + <<: *aci_fabric_node_control + state: absent + register: cm_remove_fabric_node_control + check_mode: true + +- name: Remove Fabric Node Control + cisco.aci.aci_fabric_node_control: + <<: *aci_fabric_node_control + state: absent + register: nm_remove_fabric_node_control + +- name: Remove Fabric Node Control again + cisco.aci.aci_fabric_node_control: + <<: *aci_fabric_node_control + state: absent + register: nm__remove_fabric_node_control_again + +- name: Verify Fabric Node Control removal + ansible.builtin.assert: + that: + - cm_remove_fabric_node_control is changed + - cm_remove_fabric_node_control.proposed == {} + - cm_remove_fabric_node_control.previous.0.fabricNodeControl.attributes.control == "Dom" + - cm_remove_fabric_node_control.previous.0.fabricNodeControl.attributes.descr == "Updated Ansible Test Fabric Node Control" + - cm_remove_fabric_node_control.previous.0.fabricNodeControl.attributes.featureSel == "analytics" + - cm_remove_fabric_node_control.previous.0.fabricNodeControl.attributes.name == "ansible_fabric_node_control_1" + - nm_remove_fabric_node_control is changed + - nm_remove_fabric_node_control.current == [] + - nm_remove_fabric_node_control.previous.0.fabricNodeControl.attributes.control == "Dom" + - nm_remove_fabric_node_control.previous.0.fabricNodeControl.attributes.descr == "Updated Ansible Test Fabric Node Control" + - nm_remove_fabric_node_control.previous.0.fabricNodeControl.attributes.featureSel == "analytics" + - nm_remove_fabric_node_control.previous.0.fabricNodeControl.attributes.name == "ansible_fabric_node_control_1" + - nm__remove_fabric_node_control_again is not changed + - nm__remove_fabric_node_control_again.current == [] + - nm__remove_fabric_node_control_again.previous == [] + +# CLEAN ENVIRONMENT +- name: Remove Fabric Node Control + cisco.aci.aci_fabric_node_control: + <<: *aci_info + name: "{{ item }}" + state: absent + loop: + - ansible_fabric_node_control_1 + - ansible_fabric_node_control_2 + - ansible_fabric_node_control_3