From 5515a73a8813b7b17111705111dca1a01c7d125a Mon Sep 17 00:00:00 2001 From: anvitha-jain Date: Thu, 22 Aug 2024 10:41:28 -0700 Subject: [PATCH 1/9] [minor_changes] Added new module ndo_mac_sec_policy (for macsecPolicies object). --- plugins/module_utils/constants.py | 3 + plugins/modules/ndo_mac_sec_policy.py | 364 ++++++++++++++++++ .../targets/ndo_mac_sec_policy/aliases | 2 + .../targets/ndo_mac_sec_policy/tasks/main.yml | 97 +++++ 4 files changed, 466 insertions(+) create mode 100644 plugins/modules/ndo_mac_sec_policy.py create mode 100644 tests/integration/targets/ndo_mac_sec_policy/aliases create mode 100644 tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml diff --git a/plugins/module_utils/constants.py b/plugins/module_utils/constants.py index a82ae1a6..84a64a12 100644 --- a/plugins/module_utils/constants.py +++ b/plugins/module_utils/constants.py @@ -25,6 +25,9 @@ NDO_API_VERSION_FORMAT = "/mso/api/{api_version}" NDO_API_VERSION_PATH_FORMAT = "/mso/api/{api_version}/{path}" +NDO_CIPHER_SUITE_MAP = {"128_gcm_aes": "128GcmAes", "128_gcm_aes_xpn": "128GcmAesXpn", "256_gcm_aes": "256GcmAes", "256_gcm_aes_xpn": "256GcmAesXpn"} +NDO_SECURITY_POLICY_MAP = {"should_secure": "shouldSecure", "must_secure": "mustSecure"} + EPG_U_SEG_ATTR_TYPE_MAP = { "ip": "ip", "mac": "mac", diff --git a/plugins/modules/ndo_mac_sec_policy.py b/plugins/modules/ndo_mac_sec_policy.py new file mode 100644 index 00000000..9a579e19 --- /dev/null +++ b/plugins/modules/ndo_mac_sec_policy.py @@ -0,0 +1,364 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2024, Anvitha Jain (@anvjain) + +# 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 +import copy + +__metaclass__ = type + +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "community"} + +DOCUMENTATION = r""" +--- +module: ndo_mac_sec_policy +short_description: Manage MACSec Policies on Cisco Nexus Dashboard Orchestrator (NDO). +description: +- Manage MACSec Policies on Cisco Nexus Dashboard Orchestrator (NDO). +- This module is only supported on ND v3.1 (NDO v4.3) and later. +author: +- Anvitha Jain (@anvjain) +options: + template: + description: + - The name of the template. + - The template must be a fabric policy template. + type: str + required: true + mac_sec_policy: + description: + - The name of the MACSec Policy. + type: str + aliases: [ name ] + mac_sec_policy_uuid: + description: + - The uuid of the MACSec Policy. + - This parameter is required when the O(mac_sec_policy) needs to be updated. + type: str + aliases: [ uuid ] + description: + description: + - The description of the MACSec Policy. + type: str + admin_state: + description: + - The administrative state of the MACSec Policy. (Enables or disables the policy) + type: str + choices: [ enabled, disabled ] + default: enabled + type: + description: + - The type of the interfaces this policy will be applied to. + type: str + choices: [ fabric, access ] + default: fabric + cipher_suite: + description: + - The cipher suite to be used for encryption. + type: str + choices: [ 128_gcm_aes, 128_gcm_aes_xpn, 256_gcm_aes, 256_gcm_aes_xpn ] + default: 256_gcm_aes_xpn + window_size: + description: + - The window size for the MACSec Policy. + type: int + default: 64 + security_policy: + description: + - The security policy to allow trafic on the link for the MACSec Policy. + type: str + choices: [ should_secure, must_secure ] + default: should_secure + sak_expiry_time: + description: + - The expiry time for the Security Association Key (SAK) for the MACSec Policy. + - The value must be 0 or between 60 and 2592000 + type: int + default: 0 + confidentiality_offset: + description: + - The confidentiality offset for the MACSec Policy. + type: int + choices: [0, 30, 50] + default: offset0 + key_server_priority: + description: + - The key server priority for the MACSec Policy. + - The value must be between 0 and 255 + type: int + default: 16 + mac_sec_key: + description: + - List of the MACSec Keys. + type: list + elements: dict + suboptions: + key_name: + description: + - The name of the MACSec Key. + - Key Name has to be Hex chars [0-9a-fA-F] + type: str + required: true + psk: + description: + - The Pre-Shared Key (PSK) for the MACSec Key. + - PSK has to be 64 chars long. + - PSK has to be Hex chars [0-9a-fA-F] + type: str + required: true + start_time: + description: + - The start time for the MACSec Key. + - The date time format - YYYY-MM-DD HH:MM:SS or 'now' + type: str + default: now + end_time: + description: + - The end time for the MACSec Key. + - The date time format - YYYY-MM-DD HH:MM:SS or 'infinite' + type: str + default: infinite + + + state: + description: + - Use C(absent) for removing. + - Use C(query) for listing an object or multiple objects. + - Use C(present) for creating or updating. + type: str + choices: [ absent, query, present ] + default: query +extends_documentation_fragment: cisco.mso.modules +""" + +EXAMPLES = r""" +- name: Create a new MACSec Policy + cisco.mso.ndo_mac_sec_policy: + host: mso_host + username: admin + password: SomeSecretPassword + template: ansible_test_template + mac_sec_policy: ansible_test_mac_sec_policy + description: "Ansible Test MACSec Policy" + state: present + +- name: Query a MACSec Policy + cisco.mso.ndo_mac_sec_policy: + host: mso_host + username: admin + password: SomeSecretPassword + template: ansible_test_template + mac_sec_policy: ansible_test_mac_sec_policy + state: query + register: query_one + +- name: Query all MACSec Policies + cisco.mso.ndo_mac_sec_policy: + host: mso_host + username: admin + password: SomeSecretPassword + state: query + register: query_all + +- name: Delete a MACSec Policy + cisco.mso.ndo_mac_sec_policy: + host: mso_host + username: admin + password: SomeSecretPassword + template: ansible_test_template + mac_sec_policy: ansible_test_mac_sec_policy + state: absent +""" + +RETURN = r""" +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.mso.plugins.module_utils.mso import MSOModule, mso_argument_spec +from ansible_collections.cisco.mso.plugins.module_utils.template import MSOTemplate, KVPair +from ansible_collections.cisco.mso.plugins.module_utils.constants import NDO_CIPHER_SUITE_MAP, NDO_SECURITY_POLICY_MAP + + +def main(): + argument_spec = mso_argument_spec() + argument_spec.update( + dict( + template=dict(type="str", required=True), + mac_sec_policy=dict(type="str", aliases=["name"]), + mac_sec_policy_uuid=dict(type="str", aliases=["uuid"]), + description=dict(type="str"), + admin_state=dict(type="str", choices=["enabled", "disabled"], default="enabled"), + type=dict(type="str", choices=["fabric", "access"], default="fabric"), + cipher_suite=dict(type="str", choices=["128_gcm_aes", "128_gcm_aes_xpn", "256_gcm_aes", "256_gcm_aes_xpn"], default="256_gcm_aes_xpn"), + window_size=dict(type="int", default=64), + security_policy=dict(type="str", choices=["should_secure", "must_secure"], default="should_secure"), + sak_expiry_time=dict(type="int", default=0), + confidentiality_offset=dict(type="int", choices=[0, 30, 50], default=0), + key_server_priority=dict(type="int", default=16), + mac_sec_key=dict( + type="list", + elements="dict", + options=dict( + key_name=dict(type="str", required=True), + psk=dict(type="str", required=True), + start_time=dict(type="str", default="now"), + end_time=dict(type="str", default="infinite"), + ), + ), + state=dict(type="str", choices=["absent", "query", "present"], default="query"), + ) + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "present", ["mac_sec_policy"]], + ["state", "absent", ["mac_sec_policy"]], + ] + ) + + mso = MSOModule(module) + mso.stdout = str(" ANVITHA ") + + template = module.params.get("template") + mac_sec_policy = module.params.get("mac_sec_policy") + mac_sec_policy_uuid = module.params.get("mac_sec_policy_uuid") + description = module.params.get("description") + admin_state = module.params.get("admin_state") + type = module.params.get("type") + cipher_suite = module.params.get("cipher_suite") + window_size = module.params.get("window_size") + security_policy = module.params.get("security_policy") + sak_expiry_time = module.params.get("sak_expiry_time") + confidentiality_offset = "offset" + str(module.params.get("confidentiality_offset")) + key_server_priority = module.params.get("key_server_priority") + mac_sec_keys = module.params.get("mac_sec_key") + state = module.params.get("state") + + ops = [] + match = None + + mso_template = MSOTemplate(mso, "fabric_policy", template) + mso_template.validate_template("fabricPolicy") + + path = "/fabricPolicyTemplate/template/macsecPolicies" + + existing_mac_sec_policies = mso_template.template.get("fabricPolicyTemplate", {}).get("template", {}).get("macsecPolicies", []) + mso.stdout += str("\n existing_mac_sec_policies ") + str(existing_mac_sec_policies) + if mac_sec_policy: + object_description = "MACSec Policy" + if mac_sec_policy_uuid: + match = mso_template.get_object_by_uuid(object_description, existing_mac_sec_policies, mac_sec_policy_uuid) + else: + kv_list = [KVPair("name", mac_sec_policy)] + match = mso_template.get_object_by_key_value_pairs(object_description, existing_mac_sec_policies, kv_list) + if match: + mso.existing = mso.previous = copy.deepcopy(match.details) + else: + mso.existing = mso.previous = existing_mac_sec_policies + + if state == "present": + + mso.existing = {} + + if match: + mso.stdout += str("\n match ") + str(match) + + if mac_sec_policy and match.details.get("name") != mac_sec_policy: + mso.fail_json( + msg=f"MACSec Policy '{match.details.get('name')}' already exists with a different name '{mac_sec_policy}'" + ) + + if description and match.details.get("description") != description: + ops.append(mso_template.update_object_description(match, description)) + + if admin_state and match.details.get("adminState") != admin_state: + ops.append(mso_template.update_object_admin_state(match, admin_state)) + + if type and match.details.get("type") != type: + ops.append(mso_template.update_object_type(match, type)) + + + + if cipher_suite and match.details.get("cipherSuite") != cipher_suite: + ops.append(mso_template.update_object_cipher_suite(match, cipher_suite)) + + if window_size and match.details.get("windowSize") != window_size: + ops.append(mso_template.update_object_window_size(match, window_size)) + + if security_policy and match.details.get("securityPol") != security_policy: + ops.append(mso_template.update_object_security_policy(match, security_policy)) + + if sak_expiry_time and match.details.get("sakExpiryTime") != sak_expiry_time: + ops.append(mso_template.update_object_sak_expiry_time(match, sak_expiry_time)) + + if confidentiality_offset and match.details.get("confOffSet") != confidentiality_offset: + ops.append(mso_template.update_object_confidentiality_offset(match, confidentiality_offset)) + + if key_server_priority and match.details.get("keyServerPrio") != key_server_priority: + ops.append(mso_template.update_object_key_server_priority(match, key_server_priority)) + + else: + + payload = {"name": mac_sec_policy, "templateId": mso_template.template.get("templateId"), "schemaId": mso_template.template.get("schemaId")} + if description: + payload["description"] = description + payload["adminState"] = admin_state + payload["type"] = type + + mac_sec_param_map ={} + mac_sec_param_map["cipherSuite"] = NDO_CIPHER_SUITE_MAP.get(cipher_suite) + mac_sec_param_map["windowSize"] = window_size + mac_sec_param_map["securityPol"] = NDO_SECURITY_POLICY_MAP.get(security_policy) + mac_sec_param_map["sakExpiryTime"] = sak_expiry_time + + if type == "access": + mac_sec_param_map["confOffSet"] = confidentiality_offset + mac_sec_param_map["keyServerPrio"] = key_server_priority + + payload["mac_sec_param_map"] = mac_sec_param_map + + mac_sec_keys_list = [] + if mac_sec_keys: + for mac_sec_key in mac_sec_keys: + keyname = mac_sec_key.get("key_name") + psk = mac_sec_key.get("psk") + start = mac_sec_key.get("start_time") + end = mac_sec_key.get("end_time") + + mac_sec_keys_list.append( + dict( + keyname=keyname, + psk=psk, + start=start, + end=end, + ) + ) + payload["macsecKeys"] = mac_sec_keys_list + + + ops.append(dict(op="add", path="{0}/-".format(path), value=copy.deepcopy(payload))) + + mso.sanitize(payload) + + mso.existing = mso.proposed + + elif state == "absent": + if match: + ops.append(dict(op="remove", path="{0}/{1}".format(path, match.index))) + mso.existing = {} + + if not module.check_mode and ops: + mso.request(mso_template.template_path, method="PATCH", data=ops) + + mso.exit_json() + + +if __name__ == "__main__": + main() + diff --git a/tests/integration/targets/ndo_mac_sec_policy/aliases b/tests/integration/targets/ndo_mac_sec_policy/aliases new file mode 100644 index 00000000..5042c9c0 --- /dev/null +++ b/tests/integration/targets/ndo_mac_sec_policy/aliases @@ -0,0 +1,2 @@ +# No ACI MultiSite infrastructure, so not enabled +# unsupported diff --git a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml new file mode 100644 index 00000000..9a451455 --- /dev/null +++ b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml @@ -0,0 +1,97 @@ + +# Test code for the MSO modules +# Copyright: (c) 2024, Anvitha Jain (@anvjain) + +# 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 MultiSite host, username and password + ansible.builtin.fail: + msg: 'Please define the following variables: mso_hostname, mso_username and mso_password.' + when: mso_hostname is not defined or mso_username is not defined or mso_password is not defined + +# CLEAN ENVIRONMENT +- name: Set vars + ansible.builtin.set_fact: + mso_info: &mso_info + host: '{{ mso_hostname }}' + username: '{{ mso_username }}' + password: '{{ mso_password }}' + validate_certs: '{{ mso_validate_certs | default(false) }}' + use_ssl: '{{ mso_use_ssl | default(true) }}' + use_proxy: '{{ mso_use_proxy | default(true) }}' + output_level: '{{ mso_output_level | default("info") }}' + +# QUERY VERSION +- name: Query MSO version + cisco.mso.mso_version: + <<: *mso_info + state: query + register: version + + +- name: Execute tasks only for MSO version > 4.3 + when: version.current.version is version('4.3', '>=') + block: + - name: Remove fabric template + cisco.mso.ndo_template: &template_absent + <<: *mso_info + name: ansible_fabric_policy_template + type: fabric_policy + state: absent + + - name: Create a fabric template + cisco.mso.ndo_template: + <<: *mso_info + name: ansible_fabric_policy_template + type: fabric_policy + state: present + + # CREATE + - name: Create a MACsec policy (check mode) + cisco.mso.ndo_mac_sec_policy: &add_mac_sec_policy + <<: *mso_info + template: ansible_fabric_policy_template + mac_sec_policy: ansible_mac_sec_policy + state: present + check_mode: true + register: cm_add_mac_sec_policy + + - name: Create a MACsec policy + cisco.mso.ndo_mac_sec_policy: + <<: *add_mac_sec_policy + register: nm_add_mac_sec_policy + + # - name: Create MACsec policy again + # cisco.mso.ndo_mac_sec_policy: + # <<: *add_mac_sec_policy + # register: nm_add_mac_sec_policy_again + + # - name: Assert that the MACsec policy was created + # assert: + # that: + # - cm_add_mac_sec_policy is changed + # - nm_add_mac_sec_policy is changed + # - nm_add_mac_sec_policy_again is not changed + + - name: Create another MACsec policy + cisco.mso.ndo_mac_sec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + mac_sec_policy: ansible_mac_sec_policy_2 + type: access + mac_sec_key: + - key_name: abc12 + psk: 1111111111111111111111111111111111111111111111111111111111111111 + start_time: now + end_time: infinite + # - key_name: ABC99 + # psk: AA111111111111111111111111111111111111111111111111111111111111aa + # start_time: now + # end_time: infinite + # start_time: 2024-12-12 12:12:12 # YYYY-MM-DD HH:MM:SS + # end_time: 2024-12-31 23:59:59 + state: present + + # UPDATE + + From eaae252dcb7f6583709b91fec88e778a563e9415 Mon Sep 17 00:00:00 2001 From: anvitha-jain Date: Mon, 26 Aug 2024 22:33:02 -0700 Subject: [PATCH 2/9] [ignore_changes] Module tested before adding mac_sec_keys [] to MACSec Policy object. --- plugins/modules/ndo_mac_sec_policy.py | 153 ++++++++----- .../targets/ndo_mac_sec_policy/tasks/main.yml | 208 ++++++++++++++++-- 2 files changed, 283 insertions(+), 78 deletions(-) diff --git a/plugins/modules/ndo_mac_sec_policy.py b/plugins/modules/ndo_mac_sec_policy.py index 9a579e19..08594842 100644 --- a/plugins/modules/ndo_mac_sec_policy.py +++ b/plugins/modules/ndo_mac_sec_policy.py @@ -46,9 +46,9 @@ admin_state: description: - The administrative state of the MACSec Policy. (Enables or disables the policy) + - The default value is enabled. type: str choices: [ enabled, disabled ] - default: enabled type: description: - The type of the interfaces this policy will be applied to. @@ -58,38 +58,39 @@ cipher_suite: description: - The cipher suite to be used for encryption. + - The default value is 256_gcm_aes_xpn. type: str choices: [ 128_gcm_aes, 128_gcm_aes_xpn, 256_gcm_aes, 256_gcm_aes_xpn ] - default: 256_gcm_aes_xpn window_size: description: - The window size for the MACSec Policy. + - The value must be between 0 and 4294967295. + - The default value is 0. type: int - default: 64 security_policy: description: - The security policy to allow trafic on the link for the MACSec Policy. + - The default value is should_secure. type: str choices: [ should_secure, must_secure ] - default: should_secure sak_expiry_time: description: - The expiry time for the Security Association Key (SAK) for the MACSec Policy. - - The value must be 0 or between 60 and 2592000 + - The value must be 0 or between 60 and 2592000. + - The default value is 0. type: int - default: 0 confidentiality_offset: description: - The confidentiality offset for the MACSec Policy. type: int choices: [0, 30, 50] - default: offset0 + default: 0 key_server_priority: description: - The key server priority for the MACSec Policy. - - The value must be between 0 and 255 + - The value must be between 0 and 255. + - The default value is 0. type: int - default: 16 mac_sec_key: description: - List of the MACSec Keys. @@ -102,27 +103,25 @@ - Key Name has to be Hex chars [0-9a-fA-F] type: str required: true - psk: + psk: description: - The Pre-Shared Key (PSK) for the MACSec Key. - PSK has to be 64 chars long. - PSK has to be Hex chars [0-9a-fA-F] type: str required: true - start_time: + start_time: description: - The start time for the MACSec Key. - The date time format - YYYY-MM-DD HH:MM:SS or 'now' + - The default value is now. type: str - default: now - end_time: + end_time: description: - The end time for the MACSec Key. - The date time format - YYYY-MM-DD HH:MM:SS or 'infinite' + - The default value is infinite. type: str - default: infinite - - state: description: - Use C(absent) for removing. @@ -191,22 +190,22 @@ def main(): mac_sec_policy=dict(type="str", aliases=["name"]), mac_sec_policy_uuid=dict(type="str", aliases=["uuid"]), description=dict(type="str"), - admin_state=dict(type="str", choices=["enabled", "disabled"], default="enabled"), + admin_state=dict(type="str", choices=["enabled", "disabled"]), type=dict(type="str", choices=["fabric", "access"], default="fabric"), - cipher_suite=dict(type="str", choices=["128_gcm_aes", "128_gcm_aes_xpn", "256_gcm_aes", "256_gcm_aes_xpn"], default="256_gcm_aes_xpn"), - window_size=dict(type="int", default=64), - security_policy=dict(type="str", choices=["should_secure", "must_secure"], default="should_secure"), - sak_expiry_time=dict(type="int", default=0), + cipher_suite=dict(type="str", choices=["128_gcm_aes", "128_gcm_aes_xpn", "256_gcm_aes", "256_gcm_aes_xpn"]), + window_size=dict(type="int"), + security_policy=dict(type="str", choices=["should_secure", "must_secure"]), + sak_expiry_time=dict(type="int"), confidentiality_offset=dict(type="int", choices=[0, 30, 50], default=0), - key_server_priority=dict(type="int", default=16), + key_server_priority=dict(type="int"), mac_sec_key=dict( type="list", elements="dict", options=dict( key_name=dict(type="str", required=True), psk=dict(type="str", required=True), - start_time=dict(type="str", default="now"), - end_time=dict(type="str", default="infinite"), + start_time=dict(type="str"), + end_time=dict(type="str"), ), ), state=dict(type="str", choices=["absent", "query", "present"], default="query"), @@ -265,63 +264,102 @@ def main(): if state == "present": mso.existing = {} + mso.stdout += str("\n match ") + str(match) if match: - mso.stdout += str("\n match ") + str(match) if mac_sec_policy and match.details.get("name") != mac_sec_policy: - mso.fail_json( - msg=f"MACSec Policy '{match.details.get('name')}' already exists with a different name '{mac_sec_policy}'" - ) + ops.append(dict(op="replace", path="{0}/{1}/name".format(path, match.index), value=mac_sec_policy)) + match.details["name"] = mac_sec_policy if description and match.details.get("description") != description: - ops.append(mso_template.update_object_description(match, description)) + ops.append(dict(op="replace", path="{0}/{1}/description".format(path, match.index), value=description)) + match.details["description"] = description if admin_state and match.details.get("adminState") != admin_state: - ops.append(mso_template.update_object_admin_state(match, admin_state)) + ops.append(dict(op="replace", path="{0}/{1}/adminState".format(path, match.index), value=admin_state)) + match.details["adminState"] = admin_state if type and match.details.get("type") != type: - ops.append(mso_template.update_object_type(match, type)) + ops.append(dict(op="replace", path="{0}/{1}/type".format(path, match.index), value=type)) + match.details["type"] = type + if cipher_suite and match.details.get("macsecParams")["cipherSuite"] != cipher_suite: + ops.append(dict(op="replace", path="{0}/{1}/macsecParams/cipherSuite".format(path, match.index), value=NDO_CIPHER_SUITE_MAP.get(cipher_suite))) + match.details["macsecParams"]["cipherSuite"] = NDO_CIPHER_SUITE_MAP.get(cipher_suite) + if window_size and match.details.get("macsecParams")["windowSize"] != window_size: + ops.append(dict(op="replace", path="{0}/{1}/macsecParams/windowSize".format(path, match.index), value=window_size)) + match.details["macsecParams"]["windowSize"] = window_size - if cipher_suite and match.details.get("cipherSuite") != cipher_suite: - ops.append(mso_template.update_object_cipher_suite(match, cipher_suite)) + if security_policy and match.details.get("macsecParams")["securityPol"] != security_policy: + ops.append(dict(op="replace", path="{0}/{1}/macsecParams/securityPol".format(path, match.index), value=NDO_SECURITY_POLICY_MAP.get(security_policy))) + match.details["macsecParams"]["securityPol"] = NDO_SECURITY_POLICY_MAP.get(security_policy) - if window_size and match.details.get("windowSize") != window_size: - ops.append(mso_template.update_object_window_size(match, window_size)) + if sak_expiry_time and match.details.get("macsecParams")["sakExpiryTime"] != sak_expiry_time: + ops.append(dict(op="replace", path="{0}/{1}/macsecParams/sakExpiryTime".format(path, match.index), value=sak_expiry_time)) + match.details["macsecParams"]["sakExpiryTime"] = sak_expiry_time - if security_policy and match.details.get("securityPol") != security_policy: - ops.append(mso_template.update_object_security_policy(match, security_policy)) - - if sak_expiry_time and match.details.get("sakExpiryTime") != sak_expiry_time: - ops.append(mso_template.update_object_sak_expiry_time(match, sak_expiry_time)) - - if confidentiality_offset and match.details.get("confOffSet") != confidentiality_offset: - ops.append(mso_template.update_object_confidentiality_offset(match, confidentiality_offset)) - - if key_server_priority and match.details.get("keyServerPrio") != key_server_priority: - ops.append(mso_template.update_object_key_server_priority(match, key_server_priority)) + if type == "access": + mso.stdout += ("\n IN IF access") + if confidentiality_offset and match.details.get("macsecParams")["confOffSet"] != confidentiality_offset: + ops.append(dict(op="replace", path="{0}/{1}/macsecParams/confOffSet".format(path, match.index), value=confidentiality_offset)) + match.details["macsecParams"]["confOffSet"] = confidentiality_offset + + if key_server_priority and match.details.get("macsecParams")["keyServerPrio"] != key_server_priority: + ops.append(dict(op="replace", path="{0}/{1}/macsecParams/keyServerPrio".format(path, match.index), value=key_server_priority)) + match.details["macsecParams"]["keyServerPrio"] = key_server_priority + + # if mac_sec_keys: + # mac_sec_keys_list = [] + # for mac_sec_key in mac_sec_keys: + # # if mac_sec_key.get("key_name") and match.details.get("macsecParams")["macsecKeys"]: + + # keyname = mac_sec_key.get("key_name") + # psk = mac_sec_key.get("psk") + # start = mac_sec_key.get("start_time") + # end = mac_sec_key.get("end_time") + + # mac_sec_keys_list.append( + # dict( + # keyname=keyname, + # psk=psk, + # start=start, + # end=end, + # ) + # ) + # ops.append(dict(op="replace", path="{0}/{1}/macsecKeys".format(path, match.index), value=mac_sec_keys_list)) + # match.details["macsecParams"]["macsecKeys"] = mac_sec_keys_list + + + mso.sanitize(match.details) else: + mac_sec_param_map ={} payload = {"name": mac_sec_policy, "templateId": mso_template.template.get("templateId"), "schemaId": mso_template.template.get("schemaId")} - if description: - payload["description"] = description payload["adminState"] = admin_state payload["type"] = type - - mac_sec_param_map ={} - mac_sec_param_map["cipherSuite"] = NDO_CIPHER_SUITE_MAP.get(cipher_suite) - mac_sec_param_map["windowSize"] = window_size - mac_sec_param_map["securityPol"] = NDO_SECURITY_POLICY_MAP.get(security_policy) - mac_sec_param_map["sakExpiryTime"] = sak_expiry_time + + if description: + payload["description"] = description + + if cipher_suite: + mac_sec_param_map["cipherSuite"] = NDO_CIPHER_SUITE_MAP.get(cipher_suite) + if window_size: + mac_sec_param_map["windowSize"] = window_size + if security_policy: + mac_sec_param_map["securityPol"] = NDO_SECURITY_POLICY_MAP.get(security_policy) + if sak_expiry_time: + mac_sec_param_map["sakExpiryTime"] = sak_expiry_time if type == "access": - mac_sec_param_map["confOffSet"] = confidentiality_offset - mac_sec_param_map["keyServerPrio"] = key_server_priority + if confidentiality_offset: + mac_sec_param_map["confOffSet"] = confidentiality_offset + if key_server_priority: + mac_sec_param_map["keyServerPrio"] = key_server_priority - payload["mac_sec_param_map"] = mac_sec_param_map + payload["macsecParams"] = mac_sec_param_map mac_sec_keys_list = [] if mac_sec_keys: @@ -355,6 +393,7 @@ def main(): if not module.check_mode and ops: mso.request(mso_template.template_path, method="PATCH", data=ops) + mso.stdout += str("\n\n ops ") + str(ops) + str("\n request ") + str(mso_template.template_path) mso.exit_json() diff --git a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml index 9a451455..06155174 100644 --- a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml +++ b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml @@ -19,7 +19,7 @@ validate_certs: '{{ mso_validate_certs | default(false) }}' use_ssl: '{{ mso_use_ssl | default(true) }}' use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("info") }}' + output_level: '{{ mso_output_level | default("debug") }}' # QUERY VERSION - name: Query MSO version @@ -47,7 +47,7 @@ state: present # CREATE - - name: Create a MACsec policy (check mode) + - name: Create a MACsec policy of type 'fabric' (check mode) cisco.mso.ndo_mac_sec_policy: &add_mac_sec_policy <<: *mso_info template: ansible_fabric_policy_template @@ -56,34 +56,43 @@ check_mode: true register: cm_add_mac_sec_policy - - name: Create a MACsec policy + - name: Create a MACsec policy of type 'fabric' cisco.mso.ndo_mac_sec_policy: <<: *add_mac_sec_policy register: nm_add_mac_sec_policy - # - name: Create MACsec policy again - # cisco.mso.ndo_mac_sec_policy: - # <<: *add_mac_sec_policy - # register: nm_add_mac_sec_policy_again + - name: Create MACsec policy again + cisco.mso.ndo_mac_sec_policy: + <<: *add_mac_sec_policy + register: nm_add_mac_sec_policy_again - # - name: Assert that the MACsec policy was created - # assert: - # that: - # - cm_add_mac_sec_policy is changed - # - nm_add_mac_sec_policy is changed - # - nm_add_mac_sec_policy_again is not changed + - name: Assert that the MACsec policy was created + assert: + that: + - cm_add_mac_sec_policy is changed + - cm_add_mac_sec_policy.previous == nm_add_mac_sec_policy.previous == {} + - cm_add_mac_sec_policy.current.name == cm_add_mac_sec_policy.proposed.name == 'ansible_mac_sec_policy' + - cm_add_mac_sec_policy.current.type == cm_add_mac_sec_policy.proposed.type == 'fabric' + - cm_add_mac_sec_policy.current.macsecParams == cm_add_mac_sec_policy.proposed.macsecParams == {} + - nm_add_mac_sec_policy is changed + - nm_add_mac_sec_policy.current.name == 'ansible_mac_sec_policy' + - nm_add_mac_sec_policy.current.type == 'fabric' + - nm_add_mac_sec_policy_again is not changed + - nm_add_mac_sec_policy_again.previous.name == nm_add_mac_sec_policy_again.current.name == 'ansible_mac_sec_policy' + - nm_add_mac_sec_policy_again.previous.type == nm_add_mac_sec_policy_again.current.type == 'fabric' - - name: Create another MACsec policy - cisco.mso.ndo_mac_sec_policy: + + - name: Create another MACsec policy of type 'access' + cisco.mso.ndo_mac_sec_policy: &add_mac_sec_policy_2 <<: *mso_info template: ansible_fabric_policy_template mac_sec_policy: ansible_mac_sec_policy_2 type: access - mac_sec_key: - - key_name: abc12 - psk: 1111111111111111111111111111111111111111111111111111111111111111 - start_time: now - end_time: infinite + # mac_sec_key: + # - key_name: abc12 + # psk: 1111111111111111111111111111111111111111111111111111111111111111 + # start_time: now + # end_time: infinite # - key_name: ABC99 # psk: AA111111111111111111111111111111111111111111111111111111111111aa # start_time: now @@ -91,7 +100,164 @@ # start_time: 2024-12-12 12:12:12 # YYYY-MM-DD HH:MM:SS # end_time: 2024-12-31 23:59:59 state: present + register: nm_add_mac_sec_policy_2 + + # UPDATE + - name: Update the MACsec policy of type 'access' (check mode) + cisco.mso.ndo_mac_sec_policy: &update_mac_sec_policy + <<: *add_mac_sec_policy_2 + description: 'Updated description' + admin_state: 'disabled' + cipher_suite: '128_gcm_aes' + window_size: 110 + security_policy: 'must_secure' + sak_expiry_time: 100 + confidentiality_offset: 30 + key_server_priority: 10 + state: present + check_mode: true + register: cm_update_mac_sec_policy + + - name: Update the MACsec policy of type 'access' + cisco.mso.ndo_mac_sec_policy: + <<: *update_mac_sec_policy + register: nm_update_mac_sec_policy + + - name: Update MACsec policy of type 'access' again + cisco.mso.ndo_mac_sec_policy: + <<: *update_mac_sec_policy + register: nm_update_mac_sec_policy_again + + - name: Assert that the MACsec policy was updated + assert: + that: + - cm_update_mac_sec_policy is changed + - cm_update_mac_sec_policy.previous.description == '' + - cm_update_mac_sec_policy.previous.adminState == 'enabled' + - cm_update_mac_sec_policy.previous.macsecParams.cipherSuite == '256GcmAesXpn' + # - cm_update_mac_sec_policy.previous.macsecParams.windowSize == 0 # -> showing 64 + - cm_update_mac_sec_policy.previous.macsecParams.securityPol == 'shouldSecure' + - cm_update_mac_sec_policy.previous.macsecParams.sakExpiryTime == 0 + - cm_update_mac_sec_policy.previous.macsecParams.confOffSet == 'offset0' + # - cm_update_mac_sec_policy.previous.macsecParams.keyServerPrio == 0 # -> showing 16 + - cm_update_mac_sec_policy.current.description == cm_update_mac_sec_policy.proposed.description == 'Updated description' + - cm_update_mac_sec_policy.current.adminState == cm_update_mac_sec_policy.proposed.adminState == 'disabled' + - cm_update_mac_sec_policy.current.macsecParams.cipherSuite == cm_update_mac_sec_policy.proposed.macsecParams.cipherSuite == '128GcmAes' + - cm_update_mac_sec_policy.current.macsecParams.windowSize == cm_update_mac_sec_policy.proposed.macsecParams.windowSize == 110 + - cm_update_mac_sec_policy.current.macsecParams.securityPol == cm_update_mac_sec_policy.proposed.macsecParams.securityPol == 'mustSecure' + - cm_update_mac_sec_policy.current.macsecParams.sakExpiryTime == cm_update_mac_sec_policy.proposed.macsecParams.sakExpiryTime == 100 + - cm_update_mac_sec_policy.current.macsecParams.confOffSet == cm_update_mac_sec_policy.proposed.macsecParams.confOffSet == 'offset30' + - cm_update_mac_sec_policy.current.macsecParams.keyServerPrio == cm_update_mac_sec_policy.proposed.macsecParams.keyServerPrio == 10 + - nm_update_mac_sec_policy is changed + - nm_update_mac_sec_policy.previous.description == '' + - nm_update_mac_sec_policy.previous.adminState == 'enabled' + - nm_update_mac_sec_policy.previous.macsecParams.cipherSuite == '256GcmAesXpn' + # - nm_update_mac_sec_policy.previous.macsecParams.windowSize == 0 # -> showing 64 + - nm_update_mac_sec_policy.previous.macsecParams.securityPol == 'shouldSecure' + - nm_update_mac_sec_policy.previous.macsecParams.sakExpiryTime == 0 + - nm_update_mac_sec_policy.previous.macsecParams.confOffSet == 'offset0' + # - nm_update_mac_sec_policy.previous.macsecParams.keyServerPrio == 0 # -> showing 16 + - nm_update_mac_sec_policy.current.description =='Updated description' + - nm_update_mac_sec_policy.current.adminState == 'disabled' + - nm_update_mac_sec_policy.current.macsecParams.cipherSuite == '128GcmAes' + - nm_update_mac_sec_policy.current.macsecParams.windowSize == 110 + - nm_update_mac_sec_policy.current.macsecParams.securityPol == 'mustSecure' + - nm_update_mac_sec_policy.current.macsecParams.sakExpiryTime == 100 + - nm_update_mac_sec_policy.current.macsecParams.confOffSet == 'offset30' + - nm_update_mac_sec_policy.current.macsecParams.keyServerPrio == 10 + - nm_update_mac_sec_policy_again is not changed + - nm_update_mac_sec_policy_again.previous.name == cm_update_mac_sec_policy.current.name == nm_update_mac_sec_policy.current.name == 'ansible_mac_sec_policy_2' + - nm_update_mac_sec_policy_again.previous.type == cm_update_mac_sec_policy.current.type == nm_update_mac_sec_policy.current.type == 'access' + - nm_update_mac_sec_policy_again.previous.description == cm_update_mac_sec_policy.current.description == nm_update_mac_sec_policy.current.description == 'Updated description' + + - name: Update the MACsec policy name + cisco.mso.ndo_mac_sec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + mac_sec_policy_uuid: '{{ nm_add_mac_sec_policy_again.current.uuid }}' + mac_sec_policy: ansible_mac_sec_policy_changed + state: present + register: nm_update_mac_sec_policy_uuid + + - name: Assert that the MACsec policy name was updated + assert: + that: + - nm_update_mac_sec_policy_uuid is changed + - nm_update_mac_sec_policy_uuid.previous.name == 'ansible_mac_sec_policy' + - nm_update_mac_sec_policy_uuid.current.name == 'ansible_mac_sec_policy_changed' + - nm_update_mac_sec_policy_uuid.current.type == nm_update_mac_sec_policy_uuid.current.type == 'fabric' + + # QUERY + - name: Query a MACsec policy + cisco.mso.ndo_mac_sec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + mac_sec_policy: ansible_mac_sec_policy_changed + state: query + register: query_one + + - name: Query all MACsec policies in a template + cisco.mso.ndo_mac_sec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + state: query + register: query_all + + - name: Assert that the MACsec policy was queried + assert: + that: + - query_one is not changed + - query_one.current.name == 'ansible_mac_sec_policy_changed' + - query_one.current.type == 'fabric' + - query_one.current.description == '' + - query_all is not changed + - query_all.current | length >= 2 + + - name: Query a syncE MACsec policy with mac_sec_policy uuid + cisco.mso.ndo_mac_sec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + mac_sec_policy_uuid: '{{ nm_update_mac_sec_policy_again.current.uuid }}' + state: query + register: query_uuid + + - name: Assert that the MACsec policy was queried with mac_sec_policy uuid + assert: + that: + - query_uuid is not changed + - query_uuid.current.name == 'ansible_mac_sec_policy_changed' + - query_uuid.current.type == 'fabric' + - query_uuid.current.description == '' + + # DELETE + - name: Delete a MACsec policy of type 'fabric' (check mode) + cisco.mso.ndo_mac_sec_policy: &delete_mac_sec_policy + <<: *mso_info + template: ansible_fabric_policy_template + mac_sec_policy: ansible_mac_sec_policy_changed + state: absent + check_mode: true + register: cm_delete_mac_sec_policy + + - name: Delete a MACsec policy of type 'fabric' + cisco.mso.ndo_mac_sec_policy: + <<: *delete_mac_sec_policy + register: nm_delete_mac_sec_policy + + - name: Delete MACsec policy of type 'fabric' again + cisco.mso.ndo_mac_sec_policy: + <<: *delete_mac_sec_policy + register: nm_delete_mac_sec_policy_again + + - name: Assert that the MACsec policy was deleted + assert: + that: + - cm_delete_mac_sec_policy is changed + - nm_delete_mac_sec_policy is changed + - nm_delete_mac_sec_policy_again is not changed + + + - # UPDATE From 3f9601c669fc115c621ff77b34c52e2ec8e5558e Mon Sep 17 00:00:00 2001 From: anvitha-jain Date: Wed, 28 Aug 2024 11:13:12 -0700 Subject: [PATCH 3/9] [ignore_changes] Fixes idempotancy of mac_sec_keys on update. --- plugins/modules/ndo_mac_sec_policy.py | 137 +++++++++--------- .../targets/ndo_mac_sec_policy/tasks/main.yml | 134 ++++++++++++----- 2 files changed, 168 insertions(+), 103 deletions(-) diff --git a/plugins/modules/ndo_mac_sec_policy.py b/plugins/modules/ndo_mac_sec_policy.py index 08594842..22d5c73f 100644 --- a/plugins/modules/ndo_mac_sec_policy.py +++ b/plugins/modules/ndo_mac_sec_policy.py @@ -6,7 +6,6 @@ # 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 -import copy __metaclass__ = type @@ -63,9 +62,10 @@ choices: [ 128_gcm_aes, 128_gcm_aes_xpn, 256_gcm_aes, 256_gcm_aes_xpn ] window_size: description: - - The window size for the MACSec Policy. + - The window size defines the maximum number of frames that can be received out of order + - before a replay attack is detected. - The value must be between 0 and 4294967295. - - The default value is 0. + - The default value is 0 for type fabric and 64 for type access. type: int security_policy: description: @@ -82,18 +82,19 @@ confidentiality_offset: description: - The confidentiality offset for the MACSec Policy. + - The value must be 0, 30 or 50. + - The default value is 0. type: int - choices: [0, 30, 50] - default: 0 key_server_priority: description: - The key server priority for the MACSec Policy. - The value must be between 0 and 255. - - The default value is 0. + - The default value is 0 for type fabric and 16 for type access. type: int mac_sec_key: description: - List of the MACSec Keys. + - Only one mac_sec_key can be added during creation. type: list elements: dict suboptions: @@ -106,7 +107,8 @@ psk: description: - The Pre-Shared Key (PSK) for the MACSec Key. - - PSK has to be 64 chars long. + - PSK has to be 64 chars long if cipher suite is 256_gcm_aes or 256_gcm_aes_xpn. + - PSK has to be 32 chars long if cipher suite is 128_gcm_aes or 128_gcm_aes_xpn. - PSK has to be Hex chars [0-9a-fA-F] type: str required: true @@ -114,6 +116,7 @@ description: - The start time for the MACSec Key. - The date time format - YYYY-MM-DD HH:MM:SS or 'now' + - The start time for each key_name should be unique. - The default value is now. type: str end_time: @@ -144,7 +147,7 @@ description: "Ansible Test MACSec Policy" state: present -- name: Query a MACSec Policy +- name: Query a MACSec Policy with mac_sec_policy name cisco.mso.ndo_mac_sec_policy: host: mso_host username: admin @@ -162,6 +165,16 @@ state: query register: query_all +- name: Query a MACSec Policy with mac_sec_policy uuid + cisco.mso.ndo_mac_sec_policy: + host: mso_host + username: admin + password: SomeSecretPassword + template: ansible_test_template + mac_sec_policy: ansible_test_mac_sec_policy + state: query + register: query_uuid + - name: Delete a MACSec Policy cisco.mso.ndo_mac_sec_policy: host: mso_host @@ -180,6 +193,7 @@ from ansible_collections.cisco.mso.plugins.module_utils.mso import MSOModule, mso_argument_spec from ansible_collections.cisco.mso.plugins.module_utils.template import MSOTemplate, KVPair from ansible_collections.cisco.mso.plugins.module_utils.constants import NDO_CIPHER_SUITE_MAP, NDO_SECURITY_POLICY_MAP +import copy def main(): @@ -196,17 +210,18 @@ def main(): window_size=dict(type="int"), security_policy=dict(type="str", choices=["should_secure", "must_secure"]), sak_expiry_time=dict(type="int"), - confidentiality_offset=dict(type="int", choices=[0, 30, 50], default=0), + confidentiality_offset=dict(type="int"), key_server_priority=dict(type="int"), mac_sec_key=dict( type="list", elements="dict", options=dict( key_name=dict(type="str", required=True), - psk=dict(type="str", required=True), + psk=dict(type="str", required=True, no_log=True), start_time=dict(type="str"), end_time=dict(type="str"), ), + no_log=False, ), state=dict(type="str", choices=["absent", "query", "present"], default="query"), ) @@ -218,11 +233,10 @@ def main(): required_if=[ ["state", "present", ["mac_sec_policy"]], ["state", "absent", ["mac_sec_policy"]], - ] + ], ) mso = MSOModule(module) - mso.stdout = str(" ANVITHA ") template = module.params.get("template") mac_sec_policy = module.params.get("mac_sec_policy") @@ -234,7 +248,7 @@ def main(): window_size = module.params.get("window_size") security_policy = module.params.get("security_policy") sak_expiry_time = module.params.get("sak_expiry_time") - confidentiality_offset = "offset" + str(module.params.get("confidentiality_offset")) + confidentiality_offset = module.params.get("confidentiality_offset") key_server_priority = module.params.get("key_server_priority") mac_sec_keys = module.params.get("mac_sec_key") state = module.params.get("state") @@ -248,8 +262,7 @@ def main(): path = "/fabricPolicyTemplate/template/macsecPolicies" existing_mac_sec_policies = mso_template.template.get("fabricPolicyTemplate", {}).get("template", {}).get("macsecPolicies", []) - mso.stdout += str("\n existing_mac_sec_policies ") + str(existing_mac_sec_policies) - if mac_sec_policy: + if mac_sec_policy or mac_sec_policy_uuid: object_description = "MACSec Policy" if mac_sec_policy_uuid: match = mso_template.get_object_by_uuid(object_description, existing_mac_sec_policies, mac_sec_policy_uuid) @@ -264,7 +277,6 @@ def main(): if state == "present": mso.existing = {} - mso.stdout += str("\n match ") + str(match) if match: @@ -281,8 +293,7 @@ def main(): match.details["adminState"] = admin_state if type and match.details.get("type") != type: - ops.append(dict(op="replace", path="{0}/{1}/type".format(path, match.index), value=type)) - match.details["type"] = type + mso.fail_json(msg="Type cannot be changed for an existing MACSec Policy.") if cipher_suite and match.details.get("macsecParams")["cipherSuite"] != cipher_suite: ops.append(dict(op="replace", path="{0}/{1}/macsecParams/cipherSuite".format(path, match.index), value=NDO_CIPHER_SUITE_MAP.get(cipher_suite))) @@ -293,7 +304,9 @@ def main(): match.details["macsecParams"]["windowSize"] = window_size if security_policy and match.details.get("macsecParams")["securityPol"] != security_policy: - ops.append(dict(op="replace", path="{0}/{1}/macsecParams/securityPol".format(path, match.index), value=NDO_SECURITY_POLICY_MAP.get(security_policy))) + ops.append( + dict(op="replace", path="{0}/{1}/macsecParams/securityPol".format(path, match.index), value=NDO_SECURITY_POLICY_MAP.get(security_policy)) + ) match.details["macsecParams"]["securityPol"] = NDO_SECURITY_POLICY_MAP.get(security_policy) if sak_expiry_time and match.details.get("macsecParams")["sakExpiryTime"] != sak_expiry_time: @@ -301,41 +314,39 @@ def main(): match.details["macsecParams"]["sakExpiryTime"] = sak_expiry_time if type == "access": - mso.stdout += ("\n IN IF access") if confidentiality_offset and match.details.get("macsecParams")["confOffSet"] != confidentiality_offset: - ops.append(dict(op="replace", path="{0}/{1}/macsecParams/confOffSet".format(path, match.index), value=confidentiality_offset)) - match.details["macsecParams"]["confOffSet"] = confidentiality_offset + ops.append( + dict(op="replace", path="{0}/{1}/macsecParams/confOffSet".format(path, match.index), value="offset" + str(confidentiality_offset)) + ) + match.details["macsecParams"]["confOffSet"] = "offset" + str(confidentiality_offset) if key_server_priority and match.details.get("macsecParams")["keyServerPrio"] != key_server_priority: ops.append(dict(op="replace", path="{0}/{1}/macsecParams/keyServerPrio".format(path, match.index), value=key_server_priority)) match.details["macsecParams"]["keyServerPrio"] = key_server_priority - # if mac_sec_keys: - # mac_sec_keys_list = [] - # for mac_sec_key in mac_sec_keys: - # # if mac_sec_key.get("key_name") and match.details.get("macsecParams")["macsecKeys"]: - - # keyname = mac_sec_key.get("key_name") - # psk = mac_sec_key.get("psk") - # start = mac_sec_key.get("start_time") - # end = mac_sec_key.get("end_time") - - # mac_sec_keys_list.append( - # dict( - # keyname=keyname, - # psk=psk, - # start=start, - # end=end, - # ) - # ) - # ops.append(dict(op="replace", path="{0}/{1}/macsecKeys".format(path, match.index), value=mac_sec_keys_list)) - # match.details["macsecParams"]["macsecKeys"] = mac_sec_keys_list - + if mac_sec_keys: + mac_sec_keys_list = [] + for mac_sec_key in mac_sec_keys: + mac_sec_keys_list.append( + dict( + keyname=mac_sec_key.get("key_name"), + psk=mac_sec_key.get("psk"), + start=mac_sec_key.get("start_time"), + end=mac_sec_key.get("end_time"), + ) + ) + + ops.append(dict(op="replace", path="{0}/{1}/macsecKeys".format(path, match.index), value=mac_sec_keys_list)) + match.details["macsecKeys"] = mac_sec_keys + elif mac_sec_keys == []: + # remove mac_sec_keys if the list is empty + ops.append(dict(op="remove", path="{0}/{1}/macsecKeys".format(path, match.index))) + match.details.pop("macsecKeys") mso.sanitize(match.details) else: - mac_sec_param_map ={} + mac_sec_param_map = {} payload = {"name": mac_sec_policy, "templateId": mso_template.template.get("templateId"), "schemaId": mso_template.template.get("schemaId")} payload["adminState"] = admin_state @@ -355,30 +366,26 @@ def main(): if type == "access": if confidentiality_offset: - mac_sec_param_map["confOffSet"] = confidentiality_offset + mac_sec_param_map["confOffSet"] = "offset" + str(confidentiality_offset) if key_server_priority: mac_sec_param_map["keyServerPrio"] = key_server_priority - payload["macsecParams"] = mac_sec_param_map - - mac_sec_keys_list = [] - if mac_sec_keys: - for mac_sec_key in mac_sec_keys: - keyname = mac_sec_key.get("key_name") - psk = mac_sec_key.get("psk") - start = mac_sec_key.get("start_time") - end = mac_sec_key.get("end_time") - - mac_sec_keys_list.append( - dict( - keyname=keyname, - psk=psk, - start=start, - end=end, - ) - ) - payload["macsecKeys"] = mac_sec_keys_list + payload["macsecParams"] = mac_sec_param_map + mac_sec_keys_list = [] + if mac_sec_keys: + for mac_sec_key in mac_sec_keys: + mac_sec_key_dict = { + "keyname": mac_sec_key.get("key_name"), + "psk": mac_sec_key.get("psk"), + } + if mac_sec_key.get("start_time"): + mac_sec_key_dict["startTime"] = mac_sec_key.get("start_time") + if mac_sec_key.get("end_time"): + mac_sec_key_dict["endTime"] = mac_sec_key.get("end_time") + mac_sec_keys_list.append(mac_sec_key_dict) + + payload["macsecKeys"] = mac_sec_keys_list ops.append(dict(op="add", path="{0}/-".format(path), value=copy.deepcopy(payload))) @@ -393,11 +400,9 @@ def main(): if not module.check_mode and ops: mso.request(mso_template.template_path, method="PATCH", data=ops) - mso.stdout += str("\n\n ops ") + str(ops) + str("\n request ") + str(mso_template.template_path) mso.exit_json() if __name__ == "__main__": main() - diff --git a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml index 06155174..ddd71c95 100644 --- a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml +++ b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml @@ -1,4 +1,3 @@ - # Test code for the MSO modules # Copyright: (c) 2024, Anvitha Jain (@anvjain) @@ -47,6 +46,8 @@ state: present # CREATE + + # MACsec policy type fabric - name: Create a MACsec policy of type 'fabric' (check mode) cisco.mso.ndo_mac_sec_policy: &add_mac_sec_policy <<: *mso_info @@ -73,36 +74,58 @@ - cm_add_mac_sec_policy.previous == nm_add_mac_sec_policy.previous == {} - cm_add_mac_sec_policy.current.name == cm_add_mac_sec_policy.proposed.name == 'ansible_mac_sec_policy' - cm_add_mac_sec_policy.current.type == cm_add_mac_sec_policy.proposed.type == 'fabric' - - cm_add_mac_sec_policy.current.macsecParams == cm_add_mac_sec_policy.proposed.macsecParams == {} - nm_add_mac_sec_policy is changed - nm_add_mac_sec_policy.current.name == 'ansible_mac_sec_policy' - nm_add_mac_sec_policy.current.type == 'fabric' - nm_add_mac_sec_policy_again is not changed - nm_add_mac_sec_policy_again.previous.name == nm_add_mac_sec_policy_again.current.name == 'ansible_mac_sec_policy' - nm_add_mac_sec_policy_again.previous.type == nm_add_mac_sec_policy_again.current.type == 'fabric' + - nm_add_mac_sec_policy_again.previous.uuid is defined + - nm_add_mac_sec_policy_again.current.uuid is defined + + # MACsec policy type access + - name: Create a MACSec policy of type 'access' + cisco.mso.ndo_mac_sec_policy: &add_mac_sec_policy_access + <<: *mso_info + template: ansible_fabric_policy_template + mac_sec_policy: ansible_mac_sec_policy_access + type: access + state: present + register: add_mac_sec_policy_access + + - name: Assert that the MACsec policy was created + assert: + that: + - add_mac_sec_policy_access is changed + - add_mac_sec_policy_access.previous == {} + - add_mac_sec_policy_access.current.name == 'ansible_mac_sec_policy_access' + - add_mac_sec_policy_access.current.type == 'access' + # UPDATE + # Only one mac_sec_key can be added during creation - name: Create another MACsec policy of type 'access' cisco.mso.ndo_mac_sec_policy: &add_mac_sec_policy_2 <<: *mso_info template: ansible_fabric_policy_template mac_sec_policy: ansible_mac_sec_policy_2 + description: 'Ansible MACsec Policy description' + admin_state: 'enabled' + cipher_suite: '256_gcm_aes' + window_size: 105 + security_policy: 'should_secure' + sak_expiry_time: 99 + confidentiality_offset: 50 + key_server_priority: 11 type: access - # mac_sec_key: - # - key_name: abc12 - # psk: 1111111111111111111111111111111111111111111111111111111111111111 - # start_time: now - # end_time: infinite - # - key_name: ABC99 - # psk: AA111111111111111111111111111111111111111111111111111111111111aa - # start_time: now - # end_time: infinite - # start_time: 2024-12-12 12:12:12 # YYYY-MM-DD HH:MM:SS - # end_time: 2024-12-31 23:59:59 + mac_sec_key: + - key_name: abc12 + psk: 'AA111111111111111111111111111111111111111111111111111111111111aa' + start_time: '2029-12-11 11:12:13' + end_time: '2030-12-11 11:12:13' state: present register: nm_add_mac_sec_policy_2 - # UPDATE - name: Update the MACsec policy of type 'access' (check mode) cisco.mso.ndo_mac_sec_policy: &update_mac_sec_policy <<: *add_mac_sec_policy_2 @@ -114,6 +137,16 @@ sak_expiry_time: 100 confidentiality_offset: 30 key_server_priority: 10 + mac_sec_key: + - key_name: abc12 + psk: 'AAabcdabcdabcdabcdabcdabcdabcdab' + start_time: '2029-12-11 11:12:13' + - key_name: ABC + psk: 'AAabcdabcdabcdabcdabcdabcdabcdab' + - key_name: aaa11 + psk: 'AAabcdabcdabcdabcdabcdabcdabcdab' + start_time: '2025-10-10 10:12:13' + end_time: '2026-10-10 10:12:13' state: present check_mode: true register: cm_update_mac_sec_policy @@ -123,23 +156,25 @@ <<: *update_mac_sec_policy register: nm_update_mac_sec_policy + # Idempotency for update cannot be checked as the psk is encrypted and the encrypted value changes every time - name: Update MACsec policy of type 'access' again cisco.mso.ndo_mac_sec_policy: <<: *update_mac_sec_policy + ignore_errors: true register: nm_update_mac_sec_policy_again - name: Assert that the MACsec policy was updated assert: that: - cm_update_mac_sec_policy is changed - - cm_update_mac_sec_policy.previous.description == '' + - cm_update_mac_sec_policy.previous.description == 'Ansible MACsec Policy description' - cm_update_mac_sec_policy.previous.adminState == 'enabled' - - cm_update_mac_sec_policy.previous.macsecParams.cipherSuite == '256GcmAesXpn' - # - cm_update_mac_sec_policy.previous.macsecParams.windowSize == 0 # -> showing 64 + - cm_update_mac_sec_policy.previous.macsecParams.cipherSuite == '256GcmAes' + - cm_update_mac_sec_policy.previous.macsecParams.windowSize == 105 - cm_update_mac_sec_policy.previous.macsecParams.securityPol == 'shouldSecure' - - cm_update_mac_sec_policy.previous.macsecParams.sakExpiryTime == 0 - - cm_update_mac_sec_policy.previous.macsecParams.confOffSet == 'offset0' - # - cm_update_mac_sec_policy.previous.macsecParams.keyServerPrio == 0 # -> showing 16 + - cm_update_mac_sec_policy.previous.macsecParams.sakExpiryTime == 99 + - cm_update_mac_sec_policy.previous.macsecParams.confOffSet == 'offset50' + - cm_update_mac_sec_policy.previous.macsecParams.keyServerPrio == 11 - cm_update_mac_sec_policy.current.description == cm_update_mac_sec_policy.proposed.description == 'Updated description' - cm_update_mac_sec_policy.current.adminState == cm_update_mac_sec_policy.proposed.adminState == 'disabled' - cm_update_mac_sec_policy.current.macsecParams.cipherSuite == cm_update_mac_sec_policy.proposed.macsecParams.cipherSuite == '128GcmAes' @@ -149,14 +184,14 @@ - cm_update_mac_sec_policy.current.macsecParams.confOffSet == cm_update_mac_sec_policy.proposed.macsecParams.confOffSet == 'offset30' - cm_update_mac_sec_policy.current.macsecParams.keyServerPrio == cm_update_mac_sec_policy.proposed.macsecParams.keyServerPrio == 10 - nm_update_mac_sec_policy is changed - - nm_update_mac_sec_policy.previous.description == '' + - nm_update_mac_sec_policy.previous.description == 'Ansible MACsec Policy description' - nm_update_mac_sec_policy.previous.adminState == 'enabled' - - nm_update_mac_sec_policy.previous.macsecParams.cipherSuite == '256GcmAesXpn' - # - nm_update_mac_sec_policy.previous.macsecParams.windowSize == 0 # -> showing 64 + - nm_update_mac_sec_policy.previous.macsecParams.cipherSuite == '256GcmAes' + - nm_update_mac_sec_policy.previous.macsecParams.windowSize == 105 - nm_update_mac_sec_policy.previous.macsecParams.securityPol == 'shouldSecure' - - nm_update_mac_sec_policy.previous.macsecParams.sakExpiryTime == 0 - - nm_update_mac_sec_policy.previous.macsecParams.confOffSet == 'offset0' - # - nm_update_mac_sec_policy.previous.macsecParams.keyServerPrio == 0 # -> showing 16 + - nm_update_mac_sec_policy.previous.macsecParams.sakExpiryTime == 99 + - nm_update_mac_sec_policy.previous.macsecParams.confOffSet == 'offset50' + - nm_update_mac_sec_policy.previous.macsecParams.keyServerPrio == 11 - nm_update_mac_sec_policy.current.description =='Updated description' - nm_update_mac_sec_policy.current.adminState == 'disabled' - nm_update_mac_sec_policy.current.macsecParams.cipherSuite == '128GcmAes' @@ -165,7 +200,7 @@ - nm_update_mac_sec_policy.current.macsecParams.sakExpiryTime == 100 - nm_update_mac_sec_policy.current.macsecParams.confOffSet == 'offset30' - nm_update_mac_sec_policy.current.macsecParams.keyServerPrio == 10 - - nm_update_mac_sec_policy_again is not changed + - nm_update_mac_sec_policy_again is changed - nm_update_mac_sec_policy_again.previous.name == cm_update_mac_sec_policy.current.name == nm_update_mac_sec_policy.current.name == 'ansible_mac_sec_policy_2' - nm_update_mac_sec_policy_again.previous.type == cm_update_mac_sec_policy.current.type == nm_update_mac_sec_policy.current.type == 'access' - nm_update_mac_sec_policy_again.previous.description == cm_update_mac_sec_policy.current.description == nm_update_mac_sec_policy.current.description == 'Updated description' @@ -187,8 +222,22 @@ - nm_update_mac_sec_policy_uuid.current.name == 'ansible_mac_sec_policy_changed' - nm_update_mac_sec_policy_uuid.current.type == nm_update_mac_sec_policy_uuid.current.type == 'fabric' + - name: Update the MACsec policy by removing the mac_sec_key + cisco.mso.ndo_mac_sec_policy: + <<: *update_mac_sec_policy + mac_sec_key: [] + state: present + register: rm_update_mac_sec_policy_key + + - name: Assert that the MACsec policy was updated by removing the mac_sec_key + assert: + that: + - rm_update_mac_sec_policy_key is changed + - rm_update_mac_sec_policy_key.previous.macsecKeys | length == 3 + - rm_update_mac_sec_policy_key.current.macsecKeys is not defined + # QUERY - - name: Query a MACsec policy + - name: Query a MACsec policy with mac_sec_policy name cisco.mso.ndo_mac_sec_policy: <<: *mso_info template: ansible_fabric_policy_template @@ -213,11 +262,11 @@ - query_all is not changed - query_all.current | length >= 2 - - name: Query a syncE MACsec policy with mac_sec_policy uuid + - name: Query a MACsec policy with mac_sec_policy uuid cisco.mso.ndo_mac_sec_policy: <<: *mso_info template: ansible_fabric_policy_template - mac_sec_policy_uuid: '{{ nm_update_mac_sec_policy_again.current.uuid }}' + mac_sec_policy_uuid: '{{ nm_update_mac_sec_policy_uuid.current.uuid }}' state: query register: query_uuid @@ -229,6 +278,23 @@ - query_uuid.current.type == 'fabric' - query_uuid.current.description == '' + # ERROR + - name: Update the type of exisiting MACsec policy + cisco.mso.ndo_mac_sec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + mac_sec_policy: ansible_mac_sec_policy_changed + type: access + state: present + ignore_errors: true + register: nm_update_mac_sec_policy_type + + - name: Assert that the MACsec policy type cannot be updated + assert: + that: + - nm_update_mac_sec_policy_type is failed + - nm_update_mac_sec_policy_type.msg == 'Type cannot be changed for an existing MACSec Policy.' + # DELETE - name: Delete a MACsec policy of type 'fabric' (check mode) cisco.mso.ndo_mac_sec_policy: &delete_mac_sec_policy @@ -254,10 +320,4 @@ that: - cm_delete_mac_sec_policy is changed - nm_delete_mac_sec_policy is changed - - nm_delete_mac_sec_policy_again is not changed - - - - - - + - nm_delete_mac_sec_policy_again is not changed \ No newline at end of file From 6a397f7eeb0f26616368d0e56108c0ddc551c5e7 Mon Sep 17 00:00:00 2001 From: anvitha-jain Date: Thu, 12 Sep 2024 15:29:23 -0700 Subject: [PATCH 4/9] [ignore_changes] Fixed documentation and added addition checks on tasks. --- plugins/modules/ndo_mac_sec_policy.py | 34 +++++++-------- .../targets/ndo_mac_sec_policy/tasks/main.yml | 42 ++++++++++--------- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/plugins/modules/ndo_mac_sec_policy.py b/plugins/modules/ndo_mac_sec_policy.py index 22d5c73f..6c9fa9a2 100644 --- a/plugins/modules/ndo_mac_sec_policy.py +++ b/plugins/modules/ndo_mac_sec_policy.py @@ -45,10 +45,10 @@ admin_state: description: - The administrative state of the MACSec Policy. (Enables or disables the policy) - - The default value is enabled. + - The default value is C(enabled). type: str choices: [ enabled, disabled ] - type: + interface_type: description: - The type of the interfaces this policy will be applied to. type: str @@ -57,7 +57,7 @@ cipher_suite: description: - The cipher suite to be used for encryption. - - The default value is 256_gcm_aes_xpn. + - The default value is C(256_gcm_aes_xpn). type: str choices: [ 128_gcm_aes, 128_gcm_aes_xpn, 256_gcm_aes, 256_gcm_aes_xpn ] window_size: @@ -65,12 +65,12 @@ - The window size defines the maximum number of frames that can be received out of order - before a replay attack is detected. - The value must be between 0 and 4294967295. - - The default value is 0 for type fabric and 64 for type access. + - The default value is 0 for type C(fabric) and 64 for type C(access). type: int security_policy: description: - The security policy to allow trafic on the link for the MACSec Policy. - - The default value is should_secure. + - The default value is C(should_secure). type: str choices: [ should_secure, must_secure ] sak_expiry_time: @@ -89,7 +89,7 @@ description: - The key server priority for the MACSec Policy. - The value must be between 0 and 255. - - The default value is 0 for type fabric and 16 for type access. + - The default value is 0 for type C(fabric) and 16 for type C(access). type: int mac_sec_key: description: @@ -107,8 +107,8 @@ psk: description: - The Pre-Shared Key (PSK) for the MACSec Key. - - PSK has to be 64 chars long if cipher suite is 256_gcm_aes or 256_gcm_aes_xpn. - - PSK has to be 32 chars long if cipher suite is 128_gcm_aes or 128_gcm_aes_xpn. + - PSK has to be 64 chars long if cipher suite is C(256_gcm_aes) or C(256_gcm_aes_xpn). + - PSK has to be 32 chars long if cipher suite is C(128_gcm_aes) or C(128_gcm_aes_xpn). - PSK has to be Hex chars [0-9a-fA-F] type: str required: true @@ -117,13 +117,13 @@ - The start time for the MACSec Key. - The date time format - YYYY-MM-DD HH:MM:SS or 'now' - The start time for each key_name should be unique. - - The default value is now. + - The default value is C(now). type: str end_time: description: - The end time for the MACSec Key. - The date time format - YYYY-MM-DD HH:MM:SS or 'infinite' - - The default value is infinite. + - The default value is C(infinite). type: str state: description: @@ -171,7 +171,7 @@ username: admin password: SomeSecretPassword template: ansible_test_template - mac_sec_policy: ansible_test_mac_sec_policy + mac_sec_policy_uuid: ansible_test_mac_sec_policy_uuid state: query register: query_uuid @@ -205,7 +205,7 @@ def main(): mac_sec_policy_uuid=dict(type="str", aliases=["uuid"]), description=dict(type="str"), admin_state=dict(type="str", choices=["enabled", "disabled"]), - type=dict(type="str", choices=["fabric", "access"], default="fabric"), + interface_type=dict(type="str", choices=["fabric", "access"], default="fabric"), cipher_suite=dict(type="str", choices=["128_gcm_aes", "128_gcm_aes_xpn", "256_gcm_aes", "256_gcm_aes_xpn"]), window_size=dict(type="int"), security_policy=dict(type="str", choices=["should_secure", "must_secure"]), @@ -243,7 +243,7 @@ def main(): mac_sec_policy_uuid = module.params.get("mac_sec_policy_uuid") description = module.params.get("description") admin_state = module.params.get("admin_state") - type = module.params.get("type") + interface_type = module.params.get("interface_type") cipher_suite = module.params.get("cipher_suite") window_size = module.params.get("window_size") security_policy = module.params.get("security_policy") @@ -292,7 +292,7 @@ def main(): ops.append(dict(op="replace", path="{0}/{1}/adminState".format(path, match.index), value=admin_state)) match.details["adminState"] = admin_state - if type and match.details.get("type") != type: + if interface_type and match.details.get("type") != interface_type: mso.fail_json(msg="Type cannot be changed for an existing MACSec Policy.") if cipher_suite and match.details.get("macsecParams")["cipherSuite"] != cipher_suite: @@ -313,7 +313,7 @@ def main(): ops.append(dict(op="replace", path="{0}/{1}/macsecParams/sakExpiryTime".format(path, match.index), value=sak_expiry_time)) match.details["macsecParams"]["sakExpiryTime"] = sak_expiry_time - if type == "access": + if interface_type == "access": if confidentiality_offset and match.details.get("macsecParams")["confOffSet"] != confidentiality_offset: ops.append( dict(op="replace", path="{0}/{1}/macsecParams/confOffSet".format(path, match.index), value="offset" + str(confidentiality_offset)) @@ -350,7 +350,7 @@ def main(): payload = {"name": mac_sec_policy, "templateId": mso_template.template.get("templateId"), "schemaId": mso_template.template.get("schemaId")} payload["adminState"] = admin_state - payload["type"] = type + payload["type"] = interface_type if description: payload["description"] = description @@ -364,7 +364,7 @@ def main(): if sak_expiry_time: mac_sec_param_map["sakExpiryTime"] = sak_expiry_time - if type == "access": + if interface_type == "access": if confidentiality_offset: mac_sec_param_map["confOffSet"] = "offset" + str(confidentiality_offset) if key_server_priority: diff --git a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml index ddd71c95..79974a39 100644 --- a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml +++ b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml @@ -35,20 +35,20 @@ cisco.mso.ndo_template: &template_absent <<: *mso_info name: ansible_fabric_policy_template - type: fabric_policy + interface_type: fabric_policy state: absent - name: Create a fabric template cisco.mso.ndo_template: <<: *mso_info name: ansible_fabric_policy_template - type: fabric_policy + interface_type: fabric_policy state: present # CREATE - # MACsec policy type fabric - - name: Create a MACsec policy of type 'fabric' (check mode) + # MACsec policy interface_type fabric + - name: Create a MACsec policy of interface_type 'fabric' (check mode) cisco.mso.ndo_mac_sec_policy: &add_mac_sec_policy <<: *mso_info template: ansible_fabric_policy_template @@ -57,7 +57,7 @@ check_mode: true register: cm_add_mac_sec_policy - - name: Create a MACsec policy of type 'fabric' + - name: Create a MACsec policy of interface_type 'fabric' cisco.mso.ndo_mac_sec_policy: <<: *add_mac_sec_policy register: nm_add_mac_sec_policy @@ -82,14 +82,18 @@ - nm_add_mac_sec_policy_again.previous.type == nm_add_mac_sec_policy_again.current.type == 'fabric' - nm_add_mac_sec_policy_again.previous.uuid is defined - nm_add_mac_sec_policy_again.current.uuid is defined + - nm_add_mac_sec_policy.previous.macsecParams.cipherSuite == '256GcmAesXpn' + - nm_add_mac_sec_policy.previous.macsecParams.sakExpiryTime == 0 + - nm_add_mac_sec_policy.previous.macsecParams.securityPol == 'shouldSecure' + - nm_add_mac_sec_policy.previous.macsecParams.windowSize == 0 - # MACsec policy type access - - name: Create a MACSec policy of type 'access' + # MACsec policy interface_type access + - name: Create a MACSec policy of interface_type 'access' cisco.mso.ndo_mac_sec_policy: &add_mac_sec_policy_access <<: *mso_info template: ansible_fabric_policy_template mac_sec_policy: ansible_mac_sec_policy_access - type: access + interface_type: access state: present register: add_mac_sec_policy_access @@ -104,7 +108,7 @@ # UPDATE # Only one mac_sec_key can be added during creation - - name: Create another MACsec policy of type 'access' + - name: Create another MACsec policy of interface_type 'access' cisco.mso.ndo_mac_sec_policy: &add_mac_sec_policy_2 <<: *mso_info template: ansible_fabric_policy_template @@ -117,7 +121,7 @@ sak_expiry_time: 99 confidentiality_offset: 50 key_server_priority: 11 - type: access + interface_type: access mac_sec_key: - key_name: abc12 psk: 'AA111111111111111111111111111111111111111111111111111111111111aa' @@ -126,7 +130,7 @@ state: present register: nm_add_mac_sec_policy_2 - - name: Update the MACsec policy of type 'access' (check mode) + - name: Update the MACsec policy of interface_type 'access' (check mode) cisco.mso.ndo_mac_sec_policy: &update_mac_sec_policy <<: *add_mac_sec_policy_2 description: 'Updated description' @@ -151,13 +155,13 @@ check_mode: true register: cm_update_mac_sec_policy - - name: Update the MACsec policy of type 'access' + - name: Update the MACsec policy of interface_type 'access' cisco.mso.ndo_mac_sec_policy: <<: *update_mac_sec_policy register: nm_update_mac_sec_policy # Idempotency for update cannot be checked as the psk is encrypted and the encrypted value changes every time - - name: Update MACsec policy of type 'access' again + - name: Update MACsec policy of interface_type 'access' again cisco.mso.ndo_mac_sec_policy: <<: *update_mac_sec_policy ignore_errors: true @@ -279,24 +283,24 @@ - query_uuid.current.description == '' # ERROR - - name: Update the type of exisiting MACsec policy + - name: Update the interface_type of exisiting MACsec policy cisco.mso.ndo_mac_sec_policy: <<: *mso_info template: ansible_fabric_policy_template mac_sec_policy: ansible_mac_sec_policy_changed - type: access + interface_type: access state: present ignore_errors: true register: nm_update_mac_sec_policy_type - - name: Assert that the MACsec policy type cannot be updated + - name: Assert that the MACsec policy interface_type cannot be updated assert: that: - nm_update_mac_sec_policy_type is failed - nm_update_mac_sec_policy_type.msg == 'Type cannot be changed for an existing MACSec Policy.' # DELETE - - name: Delete a MACsec policy of type 'fabric' (check mode) + - name: Delete a MACsec policy of interface_type 'fabric' (check mode) cisco.mso.ndo_mac_sec_policy: &delete_mac_sec_policy <<: *mso_info template: ansible_fabric_policy_template @@ -305,12 +309,12 @@ check_mode: true register: cm_delete_mac_sec_policy - - name: Delete a MACsec policy of type 'fabric' + - name: Delete a MACsec policy of interface_type 'fabric' cisco.mso.ndo_mac_sec_policy: <<: *delete_mac_sec_policy register: nm_delete_mac_sec_policy - - name: Delete MACsec policy of type 'fabric' again + - name: Delete MACsec policy of interface_type 'fabric' again cisco.mso.ndo_mac_sec_policy: <<: *delete_mac_sec_policy register: nm_delete_mac_sec_policy_again From d3c7858ebd768a8b132465f310377edc7df2a483 Mon Sep 17 00:00:00 2001 From: anvitha-jain Date: Tue, 17 Sep 2024 18:37:05 -0700 Subject: [PATCH 5/9] [ignore_change] Added checks to validate date_time format. --- plugins/module_utils/mso.py | 8 +++++ plugins/modules/ndo_mac_sec_policy.py | 35 +++++++++++++------ .../targets/ndo_mac_sec_policy/tasks/main.yml | 34 ++++++++++++++---- 3 files changed, 61 insertions(+), 16 deletions(-) diff --git a/plugins/module_utils/mso.py b/plugins/module_utils/mso.py index 0a916421..ca4e796a 100644 --- a/plugins/module_utils/mso.py +++ b/plugins/module_utils/mso.py @@ -1602,6 +1602,14 @@ def nd_request(self, path, method=None, data=None, file=None, qs=None, prefix="" self.fail_json(msg=msg) return {} + def verify_time_format(self, date_time): + if date_time != "now" or date_time != "infinite": + try: + formatted_date_time = datetime.datetime.strptime(date_time, "%Y-%m-%d %H:%M:%S") + return str(formatted_date_time) + except ValueError: + return self.fail_json(msg="TIME FORMAT ERROR: The time must be in 'YYYY-MM-DD HH:MM:SS' format.") + def service_node_ref_str_to_dict(serviceNodeRefStr): serviceNodeRefTokens = serviceNodeRefStr.split("/") diff --git a/plugins/modules/ndo_mac_sec_policy.py b/plugins/modules/ndo_mac_sec_policy.py index 6c9fa9a2..20e97c66 100644 --- a/plugins/modules/ndo_mac_sec_policy.py +++ b/plugins/modules/ndo_mac_sec_policy.py @@ -65,7 +65,7 @@ - The window size defines the maximum number of frames that can be received out of order - before a replay attack is detected. - The value must be between 0 and 4294967295. - - The default value is 0 for type C(fabric) and 64 for type C(access). + - The default value is 0 for type C(fabric) and 64 for type C(access). type: int security_policy: description: @@ -82,9 +82,9 @@ confidentiality_offset: description: - The confidentiality offset for the MACSec Policy. - - The value must be 0, 30 or 50. - The default value is 0. type: int + choices: [ 0, 30, 50 ] key_server_priority: description: - The key server priority for the MACSec Policy. @@ -94,7 +94,7 @@ mac_sec_key: description: - List of the MACSec Keys. - - Only one mac_sec_key can be added during creation. + - Only one mac_sec_key can be added during creation and multiple mac_sec_keys can be added during an update. type: list elements: dict suboptions: @@ -137,7 +137,7 @@ """ EXAMPLES = r""" -- name: Create a new MACSec Policy +- name: Create a new MACSec Policy of interface_type fabric cisco.mso.ndo_mac_sec_policy: host: mso_host username: admin @@ -147,6 +147,21 @@ description: "Ansible Test MACSec Policy" state: present +- name: Create a new MACSec Policy of interface_type access + cisco.mso.ndo_mac_sec_policy: + host: mso_host + username: admin + password: SomeSecretPassword + template: ansible_test_template + mac_sec_policy: ansible_test_mac_sec_policy + description: "Ansible Test MACSec Policy" + mac_sec_key: + - key_name: ansible_test_key + psk: 'AA111111111111111111111111111111111111111111111111111111111111aa' + start_time: '2029-12-11 11:12:13' + end_time: 'infinite' + state: present + - name: Query a MACSec Policy with mac_sec_policy name cisco.mso.ndo_mac_sec_policy: host: mso_host @@ -210,7 +225,7 @@ def main(): window_size=dict(type="int"), security_policy=dict(type="str", choices=["should_secure", "must_secure"]), sak_expiry_time=dict(type="int"), - confidentiality_offset=dict(type="int"), + confidentiality_offset=dict(type="int", choices=[0, 30, 50]), key_server_priority=dict(type="int"), mac_sec_key=dict( type="list", @@ -284,7 +299,7 @@ def main(): ops.append(dict(op="replace", path="{0}/{1}/name".format(path, match.index), value=mac_sec_policy)) match.details["name"] = mac_sec_policy - if description and match.details.get("description") != description: + if description is not None and match.details.get("description") != description: ops.append(dict(op="replace", path="{0}/{1}/description".format(path, match.index), value=description)) match.details["description"] = description @@ -331,8 +346,8 @@ def main(): dict( keyname=mac_sec_key.get("key_name"), psk=mac_sec_key.get("psk"), - start=mac_sec_key.get("start_time"), - end=mac_sec_key.get("end_time"), + start=mso.verify_time_format(mac_sec_key.get("start_time")) if mac_sec_key.get("start_time") else None, + end=mso.verify_time_format(mac_sec_key.get("end_time")) if mac_sec_key.get("end_time") else None, ) ) @@ -380,9 +395,9 @@ def main(): "psk": mac_sec_key.get("psk"), } if mac_sec_key.get("start_time"): - mac_sec_key_dict["startTime"] = mac_sec_key.get("start_time") + mac_sec_key_dict["startTime"] = mso.verify_time_format(mac_sec_key.get("start_time")) if mac_sec_key.get("end_time"): - mac_sec_key_dict["endTime"] = mac_sec_key.get("end_time") + mac_sec_key_dict["endTime"] = mso.verify_time_format(mac_sec_key.get("end_time")) mac_sec_keys_list.append(mac_sec_key_dict) payload["macsecKeys"] = mac_sec_keys_list diff --git a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml index 79974a39..13cdd57a 100644 --- a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml +++ b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml @@ -35,14 +35,14 @@ cisco.mso.ndo_template: &template_absent <<: *mso_info name: ansible_fabric_policy_template - interface_type: fabric_policy + type: fabric_policy state: absent - name: Create a fabric template cisco.mso.ndo_template: <<: *mso_info name: ansible_fabric_policy_template - interface_type: fabric_policy + type: fabric_policy state: present # CREATE @@ -82,10 +82,10 @@ - nm_add_mac_sec_policy_again.previous.type == nm_add_mac_sec_policy_again.current.type == 'fabric' - nm_add_mac_sec_policy_again.previous.uuid is defined - nm_add_mac_sec_policy_again.current.uuid is defined - - nm_add_mac_sec_policy.previous.macsecParams.cipherSuite == '256GcmAesXpn' - - nm_add_mac_sec_policy.previous.macsecParams.sakExpiryTime == 0 - - nm_add_mac_sec_policy.previous.macsecParams.securityPol == 'shouldSecure' - - nm_add_mac_sec_policy.previous.macsecParams.windowSize == 0 + - nm_add_mac_sec_policy_again.previous.macsecParams.cipherSuite == '256GcmAesXpn' + - nm_add_mac_sec_policy_again.previous.macsecParams.sakExpiryTime == 0 + - nm_add_mac_sec_policy_again.previous.macsecParams.securityPol == 'shouldSecure' + - nm_add_mac_sec_policy_again.previous.macsecParams.windowSize == 0 # MACsec policy interface_type access - name: Create a MACSec policy of interface_type 'access' @@ -299,6 +299,28 @@ - nm_update_mac_sec_policy_type is failed - nm_update_mac_sec_policy_type.msg == 'Type cannot be changed for an existing MACSec Policy.' + - name: Validate MACsec policy with invalide time format + cisco.mso.ndo_mac_sec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + mac_sec_policy: ansible_mac_sec_policy_2 + description: 'Ansible MACsec Policy description' + interface_type: access + mac_sec_key: + - key_name: abc12 + psk: 'AA111111111111111111111111111111111111111111111111111111111111aa' + start_time: 'wrong-time 11:12:13' + end_time: '2030-12-11 11:12:13' + state: present + ignore_errors: true + register: validate_invalid_time + + - name: Assert that the MACsec policy interface_type cannot be updated + assert: + that: + - validate_invalid_time is failed + - validate_invalid_time.msg == "TIME FORMAT ERROR{{":"}} The time must be in 'YYYY-MM-DD HH:MM:SS' format." + # DELETE - name: Delete a MACsec policy of interface_type 'fabric' (check mode) cisco.mso.ndo_mac_sec_policy: &delete_mac_sec_policy From 589a4b38e1fe1ebd559ac59828b85383419a67fa Mon Sep 17 00:00:00 2001 From: anvitha-jain Date: Tue, 24 Sep 2024 08:32:55 -0700 Subject: [PATCH 6/9] [ignore_changes] Fixed adding more than one mac_sec_keys to macSec policy during creation. --- plugins/modules/ndo_mac_sec_policy.py | 69 +++++++++---------- .../targets/ndo_mac_sec_policy/tasks/main.yml | 4 ++ 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/plugins/modules/ndo_mac_sec_policy.py b/plugins/modules/ndo_mac_sec_policy.py index 20e97c66..17726755 100644 --- a/plugins/modules/ndo_mac_sec_policy.py +++ b/plugins/modules/ndo_mac_sec_policy.py @@ -83,18 +83,19 @@ description: - The confidentiality offset for the MACSec Policy. - The default value is 0. + - This parameter is only available for type C(access). type: int choices: [ 0, 30, 50 ] key_server_priority: description: - The key server priority for the MACSec Policy. - The value must be between 0 and 255. - - The default value is 0 for type C(fabric) and 16 for type C(access). + - The default value 16 for type C(access). + - This parameter is only available for type C(access). type: int mac_sec_key: description: - List of the MACSec Keys. - - Only one mac_sec_key can be added during creation and multiple mac_sec_keys can be added during an update. type: list elements: dict suboptions: @@ -339,24 +340,24 @@ def main(): ops.append(dict(op="replace", path="{0}/{1}/macsecParams/keyServerPrio".format(path, match.index), value=key_server_priority)) match.details["macsecParams"]["keyServerPrio"] = key_server_priority - if mac_sec_keys: - mac_sec_keys_list = [] - for mac_sec_key in mac_sec_keys: - mac_sec_keys_list.append( - dict( - keyname=mac_sec_key.get("key_name"), - psk=mac_sec_key.get("psk"), - start=mso.verify_time_format(mac_sec_key.get("start_time")) if mac_sec_key.get("start_time") else None, - end=mso.verify_time_format(mac_sec_key.get("end_time")) if mac_sec_key.get("end_time") else None, - ) + if mac_sec_keys: + mac_sec_keys_list = [] + for mac_sec_key in mac_sec_keys: + mac_sec_keys_list.append( + dict( + keyname=mac_sec_key.get("key_name"), + psk=mac_sec_key.get("psk"), + start=mso.verify_time_format(mac_sec_key.get("start_time")) if mac_sec_key.get("start_time") else None, + end=mso.verify_time_format(mac_sec_key.get("end_time")) if mac_sec_key.get("end_time") else None, ) + ) - ops.append(dict(op="replace", path="{0}/{1}/macsecKeys".format(path, match.index), value=mac_sec_keys_list)) - match.details["macsecKeys"] = mac_sec_keys - elif mac_sec_keys == []: - # remove mac_sec_keys if the list is empty - ops.append(dict(op="remove", path="{0}/{1}/macsecKeys".format(path, match.index))) - match.details.pop("macsecKeys") + ops.append(dict(op="replace", path="{0}/{1}/macsecKeys".format(path, match.index), value=mac_sec_keys_list)) + match.details["macsecKeys"] = mac_sec_keys + elif mac_sec_keys == []: + # remove mac_sec_keys if the list is empty + ops.append(dict(op="remove", path="{0}/{1}/macsecKeys".format(path, match.index))) + match.details.pop("macsecKeys") mso.sanitize(match.details) @@ -364,12 +365,12 @@ def main(): mac_sec_param_map = {} payload = {"name": mac_sec_policy, "templateId": mso_template.template.get("templateId"), "schemaId": mso_template.template.get("schemaId")} - payload["adminState"] = admin_state payload["type"] = interface_type if description: payload["description"] = description - + if admin_state: + payload["adminState"] = admin_state if cipher_suite: mac_sec_param_map["cipherSuite"] = NDO_CIPHER_SUITE_MAP.get(cipher_suite) if window_size: @@ -384,23 +385,21 @@ def main(): mac_sec_param_map["confOffSet"] = "offset" + str(confidentiality_offset) if key_server_priority: mac_sec_param_map["keyServerPrio"] = key_server_priority - payload["macsecParams"] = mac_sec_param_map - mac_sec_keys_list = [] - if mac_sec_keys: - for mac_sec_key in mac_sec_keys: - mac_sec_key_dict = { - "keyname": mac_sec_key.get("key_name"), - "psk": mac_sec_key.get("psk"), - } - if mac_sec_key.get("start_time"): - mac_sec_key_dict["startTime"] = mso.verify_time_format(mac_sec_key.get("start_time")) - if mac_sec_key.get("end_time"): - mac_sec_key_dict["endTime"] = mso.verify_time_format(mac_sec_key.get("end_time")) - mac_sec_keys_list.append(mac_sec_key_dict) - - payload["macsecKeys"] = mac_sec_keys_list + mac_sec_keys_list = [] + if mac_sec_keys: + for mac_sec_key in mac_sec_keys: + mac_sec_key_dict = { + "keyname": mac_sec_key.get("key_name"), + "psk": mac_sec_key.get("psk"), + } + if mac_sec_key.get("start_time"): + mac_sec_key_dict["start"] = mac_sec_key.get("start_time") + if mac_sec_key.get("end_time"): + mac_sec_key_dict["end"] = mac_sec_key.get("end_time") + mac_sec_keys_list.append(mac_sec_key_dict) + payload["macsecKeys"] = mac_sec_keys_list ops.append(dict(op="add", path="{0}/-".format(path), value=copy.deepcopy(payload))) diff --git a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml index 13cdd57a..1b7bbfe5 100644 --- a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml +++ b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml @@ -127,6 +127,8 @@ psk: 'AA111111111111111111111111111111111111111111111111111111111111aa' start_time: '2029-12-11 11:12:13' end_time: '2030-12-11 11:12:13' + - key_name: ABC + psk: 'AAabcdabcdabcdabcdabcdabcdabcdab11111111111111111111111111111aaa' state: present register: nm_add_mac_sec_policy_2 @@ -196,6 +198,7 @@ - nm_update_mac_sec_policy.previous.macsecParams.sakExpiryTime == 99 - nm_update_mac_sec_policy.previous.macsecParams.confOffSet == 'offset50' - nm_update_mac_sec_policy.previous.macsecParams.keyServerPrio == 11 + - nm_update_mac_sec_policy.previous.macsecKeys | length == 2 - nm_update_mac_sec_policy.current.description =='Updated description' - nm_update_mac_sec_policy.current.adminState == 'disabled' - nm_update_mac_sec_policy.current.macsecParams.cipherSuite == '128GcmAes' @@ -204,6 +207,7 @@ - nm_update_mac_sec_policy.current.macsecParams.sakExpiryTime == 100 - nm_update_mac_sec_policy.current.macsecParams.confOffSet == 'offset30' - nm_update_mac_sec_policy.current.macsecParams.keyServerPrio == 10 + - nm_update_mac_sec_policy.current.macsecKeys | length == 3 - nm_update_mac_sec_policy_again is changed - nm_update_mac_sec_policy_again.previous.name == cm_update_mac_sec_policy.current.name == nm_update_mac_sec_policy.current.name == 'ansible_mac_sec_policy_2' - nm_update_mac_sec_policy_again.previous.type == cm_update_mac_sec_policy.current.type == nm_update_mac_sec_policy.current.type == 'access' From dbb02389b01b917feb00a9bccdcb6137555e4f34 Mon Sep 17 00:00:00 2001 From: anvitha-jain Date: Tue, 1 Oct 2024 13:46:55 -0700 Subject: [PATCH 7/9] [ignore_changes] Added additional changes to support delete and update with uuid. --- plugins/modules/ndo_mac_sec_policy.py | 43 +++++++--- .../targets/ndo_mac_sec_policy/tasks/main.yml | 79 +++++++++++++++---- 2 files changed, 96 insertions(+), 26 deletions(-) diff --git a/plugins/modules/ndo_mac_sec_policy.py b/plugins/modules/ndo_mac_sec_policy.py index 17726755..e189a0a0 100644 --- a/plugins/modules/ndo_mac_sec_policy.py +++ b/plugins/modules/ndo_mac_sec_policy.py @@ -178,10 +178,11 @@ host: mso_host username: admin password: SomeSecretPassword + template: ansible_test_template state: query register: query_all -- name: Query a MACSec Policy with mac_sec_policy uuid +- name: Query a MACSec Policy with mac_sec_policy UUID cisco.mso.ndo_mac_sec_policy: host: mso_host username: admin @@ -191,7 +192,7 @@ state: query register: query_uuid -- name: Delete a MACSec Policy +- name: Delete a MACSec Policy with name cisco.mso.ndo_mac_sec_policy: host: mso_host username: admin @@ -199,6 +200,15 @@ template: ansible_test_template mac_sec_policy: ansible_test_mac_sec_policy state: absent + +- name: Delete a MACSec Policy with UUID + cisco.mso.ndo_mac_sec_policy: + host: mso_host + username: admin + password: SomeSecretPassword + template: ansible_test_template + mac_sec_policy_uuid: ansible_test_mac_sec_policy_uuid + state: absent """ RETURN = r""" @@ -248,7 +258,7 @@ def main(): supports_check_mode=True, required_if=[ ["state", "present", ["mac_sec_policy"]], - ["state", "absent", ["mac_sec_policy"]], + ["state", "absent", ["mac_sec_policy", "mac_sec_policy_uuid"], True], ], ) @@ -276,15 +286,15 @@ def main(): mso_template.validate_template("fabricPolicy") path = "/fabricPolicyTemplate/template/macsecPolicies" + object_description = "MACSec Policy" existing_mac_sec_policies = mso_template.template.get("fabricPolicyTemplate", {}).get("template", {}).get("macsecPolicies", []) if mac_sec_policy or mac_sec_policy_uuid: - object_description = "MACSec Policy" - if mac_sec_policy_uuid: - match = mso_template.get_object_by_uuid(object_description, existing_mac_sec_policies, mac_sec_policy_uuid) - else: - kv_list = [KVPair("name", mac_sec_policy)] - match = mso_template.get_object_by_key_value_pairs(object_description, existing_mac_sec_policies, kv_list) + match = mso_template.get_object_by_key_value_pairs( + object_description, + existing_mac_sec_policies, + [KVPair("uuid", mac_sec_policy_uuid) if mac_sec_policy_uuid else KVPair("name", mac_sec_policy)], + ) if match: mso.existing = mso.previous = copy.deepcopy(match.details) else: @@ -410,10 +420,21 @@ def main(): elif state == "absent": if match: ops.append(dict(op="remove", path="{0}/{1}".format(path, match.index))) - mso.existing = {} if not module.check_mode and ops: - mso.request(mso_template.template_path, method="PATCH", data=ops) + response = mso.request(mso_template.template_path, method="PATCH", data=ops) + macsec_policies = response.get("fabricPolicyTemplate", {}).get("template", {}).get("macsecPolicies", []) + match = mso_template.get_object_by_key_value_pairs( + object_description, + macsec_policies, + [KVPair("uuid", mac_sec_policy_uuid) if mac_sec_policy_uuid else KVPair("name", mac_sec_policy)], + ) + if match: + mso.existing = match.details + else: + mso.existing = {} + elif module.check_mode and state != "query": + mso.existing = mso.proposed if state == "present" else {} mso.exit_json() diff --git a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml index 1b7bbfe5..17e0c63b 100644 --- a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml +++ b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml @@ -40,9 +40,7 @@ - name: Create a fabric template cisco.mso.ndo_template: - <<: *mso_info - name: ansible_fabric_policy_template - type: fabric_policy + <<: *template_absent state: present # CREATE @@ -77,15 +75,23 @@ - nm_add_mac_sec_policy is changed - nm_add_mac_sec_policy.current.name == 'ansible_mac_sec_policy' - nm_add_mac_sec_policy.current.type == 'fabric' + - nm_add_mac_sec_policy.current.description == '' + - nm_add_mac_sec_policy.current.adminState == 'enabled' + - nm_add_mac_sec_policy.current.macsecParams.cipherSuite == '256GcmAesXpn' + - nm_add_mac_sec_policy.current.macsecParams.sakExpiryTime == 0 + - nm_add_mac_sec_policy.current.macsecParams.securityPol == 'shouldSecure' + - nm_add_mac_sec_policy.current.macsecParams.windowSize == 0 + - nm_add_mac_sec_policy.current.uuid is defined - nm_add_mac_sec_policy_again is not changed - nm_add_mac_sec_policy_again.previous.name == nm_add_mac_sec_policy_again.current.name == 'ansible_mac_sec_policy' - nm_add_mac_sec_policy_again.previous.type == nm_add_mac_sec_policy_again.current.type == 'fabric' + - nm_add_mac_sec_policy_again.previous.description == nm_add_mac_sec_policy_again.current.description == '' - nm_add_mac_sec_policy_again.previous.uuid is defined - nm_add_mac_sec_policy_again.current.uuid is defined - - nm_add_mac_sec_policy_again.previous.macsecParams.cipherSuite == '256GcmAesXpn' - - nm_add_mac_sec_policy_again.previous.macsecParams.sakExpiryTime == 0 - - nm_add_mac_sec_policy_again.previous.macsecParams.securityPol == 'shouldSecure' - - nm_add_mac_sec_policy_again.previous.macsecParams.windowSize == 0 + - nm_add_mac_sec_policy_again.previous.macsecParams.cipherSuite == nm_add_mac_sec_policy_again.current.macsecParams.cipherSuite == '256GcmAesXpn' + - nm_add_mac_sec_policy_again.previous.macsecParams.sakExpiryTime == nm_add_mac_sec_policy_again.current.macsecParams.sakExpiryTime == 0 + - nm_add_mac_sec_policy_again.previous.macsecParams.securityPol == nm_add_mac_sec_policy_again.current.macsecParams.securityPol == 'shouldSecure' + - nm_add_mac_sec_policy_again.previous.macsecParams.windowSize == nm_add_mac_sec_policy_again.current.macsecParams.windowSize == 0 # MACsec policy interface_type access - name: Create a MACSec policy of interface_type 'access' @@ -104,6 +110,15 @@ - add_mac_sec_policy_access.previous == {} - add_mac_sec_policy_access.current.name == 'ansible_mac_sec_policy_access' - add_mac_sec_policy_access.current.type == 'access' + - add_mac_sec_policy_access.current.description == '' + - add_mac_sec_policy_access.current.adminState == 'enabled' + - add_mac_sec_policy_access.current.macsecParams.cipherSuite == '256GcmAesXpn' + - add_mac_sec_policy_access.current.macsecParams.sakExpiryTime == 0 + - add_mac_sec_policy_access.current.macsecParams.securityPol == 'shouldSecure' + - add_mac_sec_policy_access.current.macsecParams.windowSize == 64 + - add_mac_sec_policy_access.current.macsecParams.confOffSet == 'offset0' + - add_mac_sec_policy_access.current.macsecParams.keyServerPrio == 16 + - add_mac_sec_policy_access.current.uuid is defined # UPDATE @@ -208,10 +223,13 @@ - nm_update_mac_sec_policy.current.macsecParams.confOffSet == 'offset30' - nm_update_mac_sec_policy.current.macsecParams.keyServerPrio == 10 - nm_update_mac_sec_policy.current.macsecKeys | length == 3 + - nm_update_mac_sec_policy.current.uuid is defined - nm_update_mac_sec_policy_again is changed - nm_update_mac_sec_policy_again.previous.name == cm_update_mac_sec_policy.current.name == nm_update_mac_sec_policy.current.name == 'ansible_mac_sec_policy_2' - nm_update_mac_sec_policy_again.previous.type == cm_update_mac_sec_policy.current.type == nm_update_mac_sec_policy.current.type == 'access' - nm_update_mac_sec_policy_again.previous.description == cm_update_mac_sec_policy.current.description == nm_update_mac_sec_policy.current.description == 'Updated description' + - nm_update_mac_sec_policy_again.current.uuid is defined + - nm_update_mac_sec_policy_again.previous.uuid is defined - name: Update the MACsec policy name cisco.mso.ndo_mac_sec_policy: @@ -229,6 +247,14 @@ - nm_update_mac_sec_policy_uuid.previous.name == 'ansible_mac_sec_policy' - nm_update_mac_sec_policy_uuid.current.name == 'ansible_mac_sec_policy_changed' - nm_update_mac_sec_policy_uuid.current.type == nm_update_mac_sec_policy_uuid.current.type == 'fabric' + - nm_update_mac_sec_policy_uuid.current.description == nm_update_mac_sec_policy_uuid.current.description == '' + - nm_update_mac_sec_policy_uuid.current.adminState == nm_update_mac_sec_policy_uuid.current.adminState == 'enabled' + - nm_update_mac_sec_policy_uuid.current.macsecParams.cipherSuite == nm_update_mac_sec_policy_uuid.current.macsecParams.cipherSuite == '256GcmAesXpn' + - nm_update_mac_sec_policy_uuid.current.macsecParams.sakExpiryTime == nm_update_mac_sec_policy_uuid.current.macsecParams.sakExpiryTime == 0 + - nm_update_mac_sec_policy_uuid.current.macsecParams.securityPol == nm_update_mac_sec_policy_uuid.current.macsecParams.securityPol == 'shouldSecure' + - nm_update_mac_sec_policy_uuid.current.macsecParams.windowSize == nm_update_mac_sec_policy_uuid.current.macsecParams.windowSize == 0 + - nm_update_mac_sec_policy_uuid.previous.uuid == nm_update_mac_sec_policy_uuid.current.uuid + - nm_update_mac_sec_policy_uuid.current.uuid is defined - name: Update the MACsec policy by removing the mac_sec_key cisco.mso.ndo_mac_sec_policy: @@ -245,7 +271,7 @@ - rm_update_mac_sec_policy_key.current.macsecKeys is not defined # QUERY - - name: Query a MACsec policy with mac_sec_policy name + - name: Query a MACsec policy with name cisco.mso.ndo_mac_sec_policy: <<: *mso_info template: ansible_fabric_policy_template @@ -270,7 +296,7 @@ - query_all is not changed - query_all.current | length >= 2 - - name: Query a MACsec policy with mac_sec_policy uuid + - name: Query a MACsec policy with UUID cisco.mso.ndo_mac_sec_policy: <<: *mso_info template: ansible_fabric_policy_template @@ -278,7 +304,7 @@ state: query register: query_uuid - - name: Assert that the MACsec policy was queried with mac_sec_policy uuid + - name: Assert that the MACsec policy was queried with mac_sec_policy UUID assert: that: - query_uuid is not changed @@ -326,21 +352,21 @@ - validate_invalid_time.msg == "TIME FORMAT ERROR{{":"}} The time must be in 'YYYY-MM-DD HH:MM:SS' format." # DELETE - - name: Delete a MACsec policy of interface_type 'fabric' (check mode) + - name: Delete a MACsec policy with name (check mode) cisco.mso.ndo_mac_sec_policy: &delete_mac_sec_policy <<: *mso_info template: ansible_fabric_policy_template - mac_sec_policy: ansible_mac_sec_policy_changed + mac_sec_policy: ansible_mac_sec_policy_2 state: absent check_mode: true register: cm_delete_mac_sec_policy - - name: Delete a MACsec policy of interface_type 'fabric' + - name: Delete a MACsec policy with name cisco.mso.ndo_mac_sec_policy: <<: *delete_mac_sec_policy register: nm_delete_mac_sec_policy - - name: Delete MACsec policy of interface_type 'fabric' again + - name: Delete a MACsec policy with name again cisco.mso.ndo_mac_sec_policy: <<: *delete_mac_sec_policy register: nm_delete_mac_sec_policy_again @@ -350,4 +376,27 @@ that: - cm_delete_mac_sec_policy is changed - nm_delete_mac_sec_policy is changed - - nm_delete_mac_sec_policy_again is not changed \ No newline at end of file + - nm_delete_mac_sec_policy_again is not changed + - nm_delete_mac_sec_policy.previous.name == 'ansible_mac_sec_policy_2' + - nm_delete_mac_sec_policy.current == {} + - nm_delete_mac_sec_policy_again.current == nm_delete_mac_sec_policy_again.previous == {} + + - name: Delete a MACSec policy with UUID + cisco.mso.ndo_mac_sec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + mac_sec_policy_uuid: '{{ nm_update_mac_sec_policy_uuid.current.uuid }}' + state: absent + register: delete_mac_sec_policy_uuid + + - name: Assert that the MACsec policy was deleted using UUID + assert: + that: + - delete_mac_sec_policy_uuid is changed + - delete_mac_sec_policy_uuid.previous.name == 'ansible_mac_sec_policy_changed' + - delete_mac_sec_policy_uuid.current == {} + + # CLEANUP + - name: Remove fabric template + cisco.mso.ndo_template: + <<: *template_absent \ No newline at end of file From 85eeea157574c4034e60df6ab39bf3196a70bfdd Mon Sep 17 00:00:00 2001 From: anvitha-jain Date: Thu, 3 Oct 2024 07:21:56 -0700 Subject: [PATCH 8/9] [ignore_changes] Added comments for mac_sec_keys. --- plugins/module_utils/constants.py | 13 ++++- plugins/modules/ndo_mac_sec_policy.py | 50 +++++++++---------- .../targets/ndo_mac_sec_policy/tasks/main.yml | 14 +++--- 3 files changed, 43 insertions(+), 34 deletions(-) diff --git a/plugins/module_utils/constants.py b/plugins/module_utils/constants.py index 84a64a12..2e24247b 100644 --- a/plugins/module_utils/constants.py +++ b/plugins/module_utils/constants.py @@ -25,8 +25,17 @@ NDO_API_VERSION_FORMAT = "/mso/api/{api_version}" NDO_API_VERSION_PATH_FORMAT = "/mso/api/{api_version}/{path}" -NDO_CIPHER_SUITE_MAP = {"128_gcm_aes": "128GcmAes", "128_gcm_aes_xpn": "128GcmAesXpn", "256_gcm_aes": "256GcmAes", "256_gcm_aes_xpn": "256GcmAesXpn"} -NDO_SECURITY_POLICY_MAP = {"should_secure": "shouldSecure", "must_secure": "mustSecure"} +NDO_CIPHER_SUITE_MAP = { + "128_gcm_aes": "128GcmAes", + "128_gcm_aes_xpn": "128GcmAesXpn", + "256_gcm_aes": "256GcmAes", + "256_gcm_aes_xpn": "256GcmAesXpn", +} + +NDO_SECURITY_POLICY_MAP = { + "should_secure": "shouldSecure", + "must_secure": "mustSecure", +} EPG_U_SEG_ATTR_TYPE_MAP = { "ip": "ip", diff --git a/plugins/modules/ndo_mac_sec_policy.py b/plugins/modules/ndo_mac_sec_policy.py index e189a0a0..c95ef1fe 100644 --- a/plugins/modules/ndo_mac_sec_policy.py +++ b/plugins/modules/ndo_mac_sec_policy.py @@ -93,9 +93,11 @@ - The default value 16 for type C(access). - This parameter is only available for type C(access). type: int - mac_sec_key: + mac_sec_keys: description: - List of the MACSec Keys. + - Providing an empty list will remove the O(mac_sec_keys) from the MACSec Policy. + - The old O(mac_sec_keys) entries will be replaced with the new entries during update. type: list elements: dict suboptions: @@ -138,7 +140,7 @@ """ EXAMPLES = r""" -- name: Create a new MACSec Policy of interface_type fabric +- name: Create a new MACSec Policy of interface_type fabric cisco.mso.ndo_mac_sec_policy: host: mso_host username: admin @@ -156,7 +158,7 @@ template: ansible_test_template mac_sec_policy: ansible_test_mac_sec_policy description: "Ansible Test MACSec Policy" - mac_sec_key: + mac_sec_keys: - key_name: ansible_test_key psk: 'AA111111111111111111111111111111111111111111111111111111111111aa' start_time: '2029-12-11 11:12:13' @@ -232,13 +234,13 @@ def main(): description=dict(type="str"), admin_state=dict(type="str", choices=["enabled", "disabled"]), interface_type=dict(type="str", choices=["fabric", "access"], default="fabric"), - cipher_suite=dict(type="str", choices=["128_gcm_aes", "128_gcm_aes_xpn", "256_gcm_aes", "256_gcm_aes_xpn"]), + cipher_suite=dict(type="str", choices=list(NDO_CIPHER_SUITE_MAP)), window_size=dict(type="int"), - security_policy=dict(type="str", choices=["should_secure", "must_secure"]), + security_policy=dict(type="str", choices=list(NDO_SECURITY_POLICY_MAP)), sak_expiry_time=dict(type="int"), confidentiality_offset=dict(type="int", choices=[0, 30, 50]), key_server_priority=dict(type="int"), - mac_sec_key=dict( + mac_sec_keys=dict( type="list", elements="dict", options=dict( @@ -257,7 +259,7 @@ def main(): argument_spec=argument_spec, supports_check_mode=True, required_if=[ - ["state", "present", ["mac_sec_policy"]], + ["state", "present", ["mac_sec_policy", "mac_sec_policy_uuid"], True], ["state", "absent", ["mac_sec_policy", "mac_sec_policy_uuid"], True], ], ) @@ -270,13 +272,13 @@ def main(): description = module.params.get("description") admin_state = module.params.get("admin_state") interface_type = module.params.get("interface_type") - cipher_suite = module.params.get("cipher_suite") + cipher_suite = NDO_CIPHER_SUITE_MAP.get(module.params.get("cipher_suite")) window_size = module.params.get("window_size") - security_policy = module.params.get("security_policy") + security_policy = NDO_SECURITY_POLICY_MAP.get(module.params.get("security_policy")) sak_expiry_time = module.params.get("sak_expiry_time") confidentiality_offset = module.params.get("confidentiality_offset") key_server_priority = module.params.get("key_server_priority") - mac_sec_keys = module.params.get("mac_sec_key") + mac_sec_keys = module.params.get("mac_sec_keys") state = module.params.get("state") ops = [] @@ -302,8 +304,6 @@ def main(): if state == "present": - mso.existing = {} - if match: if mac_sec_policy and match.details.get("name") != mac_sec_policy: @@ -322,18 +322,16 @@ def main(): mso.fail_json(msg="Type cannot be changed for an existing MACSec Policy.") if cipher_suite and match.details.get("macsecParams")["cipherSuite"] != cipher_suite: - ops.append(dict(op="replace", path="{0}/{1}/macsecParams/cipherSuite".format(path, match.index), value=NDO_CIPHER_SUITE_MAP.get(cipher_suite))) - match.details["macsecParams"]["cipherSuite"] = NDO_CIPHER_SUITE_MAP.get(cipher_suite) + ops.append(dict(op="replace", path="{0}/{1}/macsecParams/cipherSuite".format(path, match.index), value=cipher_suite)) + match.details["macsecParams"]["cipherSuite"] = cipher_suite if window_size and match.details.get("macsecParams")["windowSize"] != window_size: ops.append(dict(op="replace", path="{0}/{1}/macsecParams/windowSize".format(path, match.index), value=window_size)) match.details["macsecParams"]["windowSize"] = window_size if security_policy and match.details.get("macsecParams")["securityPol"] != security_policy: - ops.append( - dict(op="replace", path="{0}/{1}/macsecParams/securityPol".format(path, match.index), value=NDO_SECURITY_POLICY_MAP.get(security_policy)) - ) - match.details["macsecParams"]["securityPol"] = NDO_SECURITY_POLICY_MAP.get(security_policy) + ops.append(dict(op="replace", path="{0}/{1}/macsecParams/securityPol".format(path, match.index), value=security_policy)) + match.details["macsecParams"]["securityPol"] = security_policy if sak_expiry_time and match.details.get("macsecParams")["sakExpiryTime"] != sak_expiry_time: ops.append(dict(op="replace", path="{0}/{1}/macsecParams/sakExpiryTime".format(path, match.index), value=sak_expiry_time)) @@ -342,15 +340,16 @@ def main(): if interface_type == "access": if confidentiality_offset and match.details.get("macsecParams")["confOffSet"] != confidentiality_offset: ops.append( - dict(op="replace", path="{0}/{1}/macsecParams/confOffSet".format(path, match.index), value="offset" + str(confidentiality_offset)) + dict(op="replace", path="{0}/{1}/macsecParams/confOffSet".format(path, match.index), value="offset{0}".format(confidentiality_offset)) ) - match.details["macsecParams"]["confOffSet"] = "offset" + str(confidentiality_offset) + match.details["macsecParams"]["confOffSet"] = "offset{0}".format(confidentiality_offset) if key_server_priority and match.details.get("macsecParams")["keyServerPrio"] != key_server_priority: ops.append(dict(op="replace", path="{0}/{1}/macsecParams/keyServerPrio".format(path, match.index), value=key_server_priority)) match.details["macsecParams"]["keyServerPrio"] = key_server_priority if mac_sec_keys: + # updating mac_sec_keys modifies the existing list with the new list mac_sec_keys_list = [] for mac_sec_key in mac_sec_keys: mac_sec_keys_list.append( @@ -362,12 +361,13 @@ def main(): ) ) - ops.append(dict(op="replace", path="{0}/{1}/macsecKeys".format(path, match.index), value=mac_sec_keys_list)) + if mac_sec_keys_list != match.details.get("macsecKeys", []): + ops.append(dict(op="replace", path="{0}/{1}/macsecKeys".format(path, match.index), value=mac_sec_keys_list)) match.details["macsecKeys"] = mac_sec_keys elif mac_sec_keys == []: # remove mac_sec_keys if the list is empty ops.append(dict(op="remove", path="{0}/{1}/macsecKeys".format(path, match.index))) - match.details.pop("macsecKeys") + match.details.pop("macsecKeys", None) mso.sanitize(match.details) @@ -382,17 +382,17 @@ def main(): if admin_state: payload["adminState"] = admin_state if cipher_suite: - mac_sec_param_map["cipherSuite"] = NDO_CIPHER_SUITE_MAP.get(cipher_suite) + mac_sec_param_map["cipherSuite"] = cipher_suite if window_size: mac_sec_param_map["windowSize"] = window_size if security_policy: - mac_sec_param_map["securityPol"] = NDO_SECURITY_POLICY_MAP.get(security_policy) + mac_sec_param_map["securityPol"] = security_policy if sak_expiry_time: mac_sec_param_map["sakExpiryTime"] = sak_expiry_time if interface_type == "access": if confidentiality_offset: - mac_sec_param_map["confOffSet"] = "offset" + str(confidentiality_offset) + mac_sec_param_map["confOffSet"] = "offset{0}".format(confidentiality_offset) if key_server_priority: mac_sec_param_map["keyServerPrio"] = key_server_priority payload["macsecParams"] = mac_sec_param_map diff --git a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml index 17e0c63b..55f92449 100644 --- a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml +++ b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml @@ -122,7 +122,7 @@ # UPDATE - # Only one mac_sec_key can be added during creation + # Only one mac_sec_keys can be added during creation - name: Create another MACsec policy of interface_type 'access' cisco.mso.ndo_mac_sec_policy: &add_mac_sec_policy_2 <<: *mso_info @@ -137,7 +137,7 @@ confidentiality_offset: 50 key_server_priority: 11 interface_type: access - mac_sec_key: + mac_sec_keys: - key_name: abc12 psk: 'AA111111111111111111111111111111111111111111111111111111111111aa' start_time: '2029-12-11 11:12:13' @@ -158,7 +158,7 @@ sak_expiry_time: 100 confidentiality_offset: 30 key_server_priority: 10 - mac_sec_key: + mac_sec_keys: - key_name: abc12 psk: 'AAabcdabcdabcdabcdabcdabcdabcdab' start_time: '2029-12-11 11:12:13' @@ -256,14 +256,14 @@ - nm_update_mac_sec_policy_uuid.previous.uuid == nm_update_mac_sec_policy_uuid.current.uuid - nm_update_mac_sec_policy_uuid.current.uuid is defined - - name: Update the MACsec policy by removing the mac_sec_key + - name: Update the MACsec policy by removing the mac_sec_keys cisco.mso.ndo_mac_sec_policy: <<: *update_mac_sec_policy - mac_sec_key: [] + mac_sec_keys: [] state: present register: rm_update_mac_sec_policy_key - - name: Assert that the MACsec policy was updated by removing the mac_sec_key + - name: Assert that the MACsec policy was updated by removing the mac_sec_keys assert: that: - rm_update_mac_sec_policy_key is changed @@ -336,7 +336,7 @@ mac_sec_policy: ansible_mac_sec_policy_2 description: 'Ansible MACsec Policy description' interface_type: access - mac_sec_key: + mac_sec_keys: - key_name: abc12 psk: 'AA111111111111111111111111111111111111111111111111111111111111aa' start_time: 'wrong-time 11:12:13' From a97dba4fbf25ec5bc9788fc5c203a764f91ec95c Mon Sep 17 00:00:00 2001 From: anvitha-jain Date: Wed, 9 Oct 2024 14:46:05 -0700 Subject: [PATCH 9/9] [ignore_changes] Renamed module and fixed documentation. --- plugins/module_utils/mso.py | 2 +- ...mac_sec_policy.py => ndo_macsec_policy.py} | 156 +++---- .../targets/ndo_mac_sec_policy/tasks/main.yml | 402 ------------------ .../aliases | 0 .../targets/ndo_macsec_policy/tasks/main.yml | 402 ++++++++++++++++++ 5 files changed, 481 insertions(+), 481 deletions(-) rename plugins/modules/{ndo_mac_sec_policy.py => ndo_macsec_policy.py} (74%) delete mode 100644 tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml rename tests/integration/targets/{ndo_mac_sec_policy => ndo_macsec_policy}/aliases (100%) create mode 100644 tests/integration/targets/ndo_macsec_policy/tasks/main.yml diff --git a/plugins/module_utils/mso.py b/plugins/module_utils/mso.py index ca4e796a..f66d4cbb 100644 --- a/plugins/module_utils/mso.py +++ b/plugins/module_utils/mso.py @@ -1608,7 +1608,7 @@ def verify_time_format(self, date_time): formatted_date_time = datetime.datetime.strptime(date_time, "%Y-%m-%d %H:%M:%S") return str(formatted_date_time) except ValueError: - return self.fail_json(msg="TIME FORMAT ERROR: The time must be in 'YYYY-MM-DD HH:MM:SS' format.") + return self.fail_json(msg="ERROR: The time must be in 'YYYY-MM-DD HH:MM:SS' format.") def service_node_ref_str_to_dict(serviceNodeRefStr): diff --git a/plugins/modules/ndo_mac_sec_policy.py b/plugins/modules/ndo_macsec_policy.py similarity index 74% rename from plugins/modules/ndo_mac_sec_policy.py rename to plugins/modules/ndo_macsec_policy.py index c95ef1fe..fafb37e1 100644 --- a/plugins/modules/ndo_mac_sec_policy.py +++ b/plugins/modules/ndo_macsec_policy.py @@ -13,7 +13,7 @@ DOCUMENTATION = r""" --- -module: ndo_mac_sec_policy +module: ndo_macsec_policy short_description: Manage MACSec Policies on Cisco Nexus Dashboard Orchestrator (NDO). description: - Manage MACSec Policies on Cisco Nexus Dashboard Orchestrator (NDO). @@ -27,15 +27,15 @@ - The template must be a fabric policy template. type: str required: true - mac_sec_policy: + macsec_policy: description: - The name of the MACSec Policy. type: str aliases: [ name ] - mac_sec_policy_uuid: + macsec_policy_uuid: description: - - The uuid of the MACSec Policy. - - This parameter is required when the O(mac_sec_policy) needs to be updated. + - The UUID of the MACSec Policy. + - This parameter is required when the O(macsec_policy) needs to be updated. type: str aliases: [ uuid ] description: @@ -69,7 +69,7 @@ type: int security_policy: description: - - The security policy to allow trafic on the link for the MACSec Policy. + - The security policy to allow traffic on the link for the MACSec Policy. - The default value is C(should_secure). type: str choices: [ should_secure, must_secure ] @@ -93,11 +93,11 @@ - The default value 16 for type C(access). - This parameter is only available for type C(access). type: int - mac_sec_keys: + macsec_keys: description: - List of the MACSec Keys. - - Providing an empty list will remove the O(mac_sec_keys) from the MACSec Policy. - - The old O(mac_sec_keys) entries will be replaced with the new entries during update. + - Providing an empty list will remove the O(macsec_keys) from the MACSec Policy. + - The old O(macsec_keys) entries will be replaced with the new entries during update. type: list elements: dict suboptions: @@ -141,42 +141,42 @@ EXAMPLES = r""" - name: Create a new MACSec Policy of interface_type fabric - cisco.mso.ndo_mac_sec_policy: + cisco.mso.ndo_macsec_policy: host: mso_host username: admin password: SomeSecretPassword template: ansible_test_template - mac_sec_policy: ansible_test_mac_sec_policy + macsec_policy: ansible_test_macsec_policy description: "Ansible Test MACSec Policy" state: present - name: Create a new MACSec Policy of interface_type access - cisco.mso.ndo_mac_sec_policy: + cisco.mso.ndo_macsec_policy: host: mso_host username: admin password: SomeSecretPassword template: ansible_test_template - mac_sec_policy: ansible_test_mac_sec_policy + macsec_policy: ansible_test_macsec_policy description: "Ansible Test MACSec Policy" - mac_sec_keys: + macsec_keys: - key_name: ansible_test_key psk: 'AA111111111111111111111111111111111111111111111111111111111111aa' start_time: '2029-12-11 11:12:13' end_time: 'infinite' state: present -- name: Query a MACSec Policy with mac_sec_policy name - cisco.mso.ndo_mac_sec_policy: +- name: Query a MACSec Policy with macsec_policy name + cisco.mso.ndo_macsec_policy: host: mso_host username: admin password: SomeSecretPassword template: ansible_test_template - mac_sec_policy: ansible_test_mac_sec_policy + macsec_policy: ansible_test_macsec_policy state: query register: query_one - name: Query all MACSec Policies - cisco.mso.ndo_mac_sec_policy: + cisco.mso.ndo_macsec_policy: host: mso_host username: admin password: SomeSecretPassword @@ -184,32 +184,32 @@ state: query register: query_all -- name: Query a MACSec Policy with mac_sec_policy UUID - cisco.mso.ndo_mac_sec_policy: +- name: Query a MACSec Policy with macsec_policy UUID + cisco.mso.ndo_macsec_policy: host: mso_host username: admin password: SomeSecretPassword template: ansible_test_template - mac_sec_policy_uuid: ansible_test_mac_sec_policy_uuid + macsec_policy_uuid: ansible_test_macsec_policy_uuid state: query register: query_uuid - name: Delete a MACSec Policy with name - cisco.mso.ndo_mac_sec_policy: + cisco.mso.ndo_macsec_policy: host: mso_host username: admin password: SomeSecretPassword template: ansible_test_template - mac_sec_policy: ansible_test_mac_sec_policy + macsec_policy: ansible_test_macsec_policy state: absent - name: Delete a MACSec Policy with UUID - cisco.mso.ndo_mac_sec_policy: + cisco.mso.ndo_macsec_policy: host: mso_host username: admin password: SomeSecretPassword template: ansible_test_template - mac_sec_policy_uuid: ansible_test_mac_sec_policy_uuid + macsec_policy_uuid: ansible_test_macsec_policy_uuid state: absent """ @@ -229,8 +229,8 @@ def main(): argument_spec.update( dict( template=dict(type="str", required=True), - mac_sec_policy=dict(type="str", aliases=["name"]), - mac_sec_policy_uuid=dict(type="str", aliases=["uuid"]), + macsec_policy=dict(type="str", aliases=["name"]), + macsec_policy_uuid=dict(type="str", aliases=["uuid"]), description=dict(type="str"), admin_state=dict(type="str", choices=["enabled", "disabled"]), interface_type=dict(type="str", choices=["fabric", "access"], default="fabric"), @@ -240,7 +240,7 @@ def main(): sak_expiry_time=dict(type="int"), confidentiality_offset=dict(type="int", choices=[0, 30, 50]), key_server_priority=dict(type="int"), - mac_sec_keys=dict( + macsec_keys=dict( type="list", elements="dict", options=dict( @@ -259,16 +259,16 @@ def main(): argument_spec=argument_spec, supports_check_mode=True, required_if=[ - ["state", "present", ["mac_sec_policy", "mac_sec_policy_uuid"], True], - ["state", "absent", ["mac_sec_policy", "mac_sec_policy_uuid"], True], + ["state", "present", ["macsec_policy", "macsec_policy_uuid"], True], + ["state", "absent", ["macsec_policy", "macsec_policy_uuid"], True], ], ) mso = MSOModule(module) template = module.params.get("template") - mac_sec_policy = module.params.get("mac_sec_policy") - mac_sec_policy_uuid = module.params.get("mac_sec_policy_uuid") + macsec_policy = module.params.get("macsec_policy") + macsec_policy_uuid = module.params.get("macsec_policy_uuid") description = module.params.get("description") admin_state = module.params.get("admin_state") interface_type = module.params.get("interface_type") @@ -278,7 +278,7 @@ def main(): sak_expiry_time = module.params.get("sak_expiry_time") confidentiality_offset = module.params.get("confidentiality_offset") key_server_priority = module.params.get("key_server_priority") - mac_sec_keys = module.params.get("mac_sec_keys") + macsec_keys = module.params.get("macsec_keys") state = module.params.get("state") ops = [] @@ -290,25 +290,25 @@ def main(): path = "/fabricPolicyTemplate/template/macsecPolicies" object_description = "MACSec Policy" - existing_mac_sec_policies = mso_template.template.get("fabricPolicyTemplate", {}).get("template", {}).get("macsecPolicies", []) - if mac_sec_policy or mac_sec_policy_uuid: + existing_macsec_policies = mso_template.template.get("fabricPolicyTemplate", {}).get("template", {}).get("macsecPolicies", []) + if macsec_policy or macsec_policy_uuid: match = mso_template.get_object_by_key_value_pairs( object_description, - existing_mac_sec_policies, - [KVPair("uuid", mac_sec_policy_uuid) if mac_sec_policy_uuid else KVPair("name", mac_sec_policy)], + existing_macsec_policies, + [KVPair("uuid", macsec_policy_uuid) if macsec_policy_uuid else KVPair("name", macsec_policy)], ) if match: mso.existing = mso.previous = copy.deepcopy(match.details) else: - mso.existing = mso.previous = existing_mac_sec_policies + mso.existing = mso.previous = existing_macsec_policies if state == "present": if match: - if mac_sec_policy and match.details.get("name") != mac_sec_policy: - ops.append(dict(op="replace", path="{0}/{1}/name".format(path, match.index), value=mac_sec_policy)) - match.details["name"] = mac_sec_policy + if macsec_policy and match.details.get("name") != macsec_policy: + ops.append(dict(op="replace", path="{0}/{1}/name".format(path, match.index), value=macsec_policy)) + match.details["name"] = macsec_policy if description is not None and match.details.get("description") != description: ops.append(dict(op="replace", path="{0}/{1}/description".format(path, match.index), value=description)) @@ -348,33 +348,33 @@ def main(): ops.append(dict(op="replace", path="{0}/{1}/macsecParams/keyServerPrio".format(path, match.index), value=key_server_priority)) match.details["macsecParams"]["keyServerPrio"] = key_server_priority - if mac_sec_keys: - # updating mac_sec_keys modifies the existing list with the new list - mac_sec_keys_list = [] - for mac_sec_key in mac_sec_keys: - mac_sec_keys_list.append( + if macsec_keys: + # updating macsec_keys modifies the existing list with the new list + macsec_keys_list = [] + for macsec_key in macsec_keys: + macsec_keys_list.append( dict( - keyname=mac_sec_key.get("key_name"), - psk=mac_sec_key.get("psk"), - start=mso.verify_time_format(mac_sec_key.get("start_time")) if mac_sec_key.get("start_time") else None, - end=mso.verify_time_format(mac_sec_key.get("end_time")) if mac_sec_key.get("end_time") else None, + keyname=macsec_key.get("key_name"), + psk=macsec_key.get("psk"), + start=mso.verify_time_format(macsec_key.get("start_time")) if macsec_key.get("start_time") else None, + end=mso.verify_time_format(macsec_key.get("end_time")) if macsec_key.get("end_time") else None, ) ) - if mac_sec_keys_list != match.details.get("macsecKeys", []): - ops.append(dict(op="replace", path="{0}/{1}/macsecKeys".format(path, match.index), value=mac_sec_keys_list)) - match.details["macsecKeys"] = mac_sec_keys - elif mac_sec_keys == []: - # remove mac_sec_keys if the list is empty + if macsec_keys_list != match.details.get("macsecKeys", []): + ops.append(dict(op="replace", path="{0}/{1}/macsecKeys".format(path, match.index), value=macsec_keys_list)) + match.details["macsecKeys"] = macsec_keys + elif macsec_keys == []: + # remove macsec_keys if the list is empty ops.append(dict(op="remove", path="{0}/{1}/macsecKeys".format(path, match.index))) match.details.pop("macsecKeys", None) mso.sanitize(match.details) else: - mac_sec_param_map = {} + macsec_param_map = {} - payload = {"name": mac_sec_policy, "templateId": mso_template.template.get("templateId"), "schemaId": mso_template.template.get("schemaId")} + payload = {"name": macsec_policy, "templateId": mso_template.template.get("templateId"), "schemaId": mso_template.template.get("schemaId")} payload["type"] = interface_type if description: @@ -382,34 +382,34 @@ def main(): if admin_state: payload["adminState"] = admin_state if cipher_suite: - mac_sec_param_map["cipherSuite"] = cipher_suite + macsec_param_map["cipherSuite"] = cipher_suite if window_size: - mac_sec_param_map["windowSize"] = window_size + macsec_param_map["windowSize"] = window_size if security_policy: - mac_sec_param_map["securityPol"] = security_policy + macsec_param_map["securityPol"] = security_policy if sak_expiry_time: - mac_sec_param_map["sakExpiryTime"] = sak_expiry_time + macsec_param_map["sakExpiryTime"] = sak_expiry_time if interface_type == "access": if confidentiality_offset: - mac_sec_param_map["confOffSet"] = "offset{0}".format(confidentiality_offset) + macsec_param_map["confOffSet"] = "offset{0}".format(confidentiality_offset) if key_server_priority: - mac_sec_param_map["keyServerPrio"] = key_server_priority - payload["macsecParams"] = mac_sec_param_map - - mac_sec_keys_list = [] - if mac_sec_keys: - for mac_sec_key in mac_sec_keys: - mac_sec_key_dict = { - "keyname": mac_sec_key.get("key_name"), - "psk": mac_sec_key.get("psk"), + macsec_param_map["keyServerPrio"] = key_server_priority + payload["macsecParams"] = macsec_param_map + + macsec_keys_list = [] + if macsec_keys: + for macsec_key in macsec_keys: + macsec_key_dict = { + "keyname": macsec_key.get("key_name"), + "psk": macsec_key.get("psk"), } - if mac_sec_key.get("start_time"): - mac_sec_key_dict["start"] = mac_sec_key.get("start_time") - if mac_sec_key.get("end_time"): - mac_sec_key_dict["end"] = mac_sec_key.get("end_time") - mac_sec_keys_list.append(mac_sec_key_dict) - payload["macsecKeys"] = mac_sec_keys_list + if macsec_key.get("start_time"): + macsec_key_dict["start"] = macsec_key.get("start_time") + if macsec_key.get("end_time"): + macsec_key_dict["end"] = macsec_key.get("end_time") + macsec_keys_list.append(macsec_key_dict) + payload["macsecKeys"] = macsec_keys_list ops.append(dict(op="add", path="{0}/-".format(path), value=copy.deepcopy(payload))) @@ -427,7 +427,7 @@ def main(): match = mso_template.get_object_by_key_value_pairs( object_description, macsec_policies, - [KVPair("uuid", mac_sec_policy_uuid) if mac_sec_policy_uuid else KVPair("name", mac_sec_policy)], + [KVPair("uuid", macsec_policy_uuid) if macsec_policy_uuid else KVPair("name", macsec_policy)], ) if match: mso.existing = match.details diff --git a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml b/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml deleted file mode 100644 index 55f92449..00000000 --- a/tests/integration/targets/ndo_mac_sec_policy/tasks/main.yml +++ /dev/null @@ -1,402 +0,0 @@ -# Test code for the MSO modules -# Copyright: (c) 2024, Anvitha Jain (@anvjain) - -# 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 MultiSite host, username and password - ansible.builtin.fail: - msg: 'Please define the following variables: mso_hostname, mso_username and mso_password.' - when: mso_hostname is not defined or mso_username is not defined or mso_password is not defined - -# CLEAN ENVIRONMENT -- name: Set vars - ansible.builtin.set_fact: - mso_info: &mso_info - host: '{{ mso_hostname }}' - username: '{{ mso_username }}' - password: '{{ mso_password }}' - validate_certs: '{{ mso_validate_certs | default(false) }}' - use_ssl: '{{ mso_use_ssl | default(true) }}' - use_proxy: '{{ mso_use_proxy | default(true) }}' - output_level: '{{ mso_output_level | default("debug") }}' - -# QUERY VERSION -- name: Query MSO version - cisco.mso.mso_version: - <<: *mso_info - state: query - register: version - - -- name: Execute tasks only for MSO version > 4.3 - when: version.current.version is version('4.3', '>=') - block: - - name: Remove fabric template - cisco.mso.ndo_template: &template_absent - <<: *mso_info - name: ansible_fabric_policy_template - type: fabric_policy - state: absent - - - name: Create a fabric template - cisco.mso.ndo_template: - <<: *template_absent - state: present - - # CREATE - - # MACsec policy interface_type fabric - - name: Create a MACsec policy of interface_type 'fabric' (check mode) - cisco.mso.ndo_mac_sec_policy: &add_mac_sec_policy - <<: *mso_info - template: ansible_fabric_policy_template - mac_sec_policy: ansible_mac_sec_policy - state: present - check_mode: true - register: cm_add_mac_sec_policy - - - name: Create a MACsec policy of interface_type 'fabric' - cisco.mso.ndo_mac_sec_policy: - <<: *add_mac_sec_policy - register: nm_add_mac_sec_policy - - - name: Create MACsec policy again - cisco.mso.ndo_mac_sec_policy: - <<: *add_mac_sec_policy - register: nm_add_mac_sec_policy_again - - - name: Assert that the MACsec policy was created - assert: - that: - - cm_add_mac_sec_policy is changed - - cm_add_mac_sec_policy.previous == nm_add_mac_sec_policy.previous == {} - - cm_add_mac_sec_policy.current.name == cm_add_mac_sec_policy.proposed.name == 'ansible_mac_sec_policy' - - cm_add_mac_sec_policy.current.type == cm_add_mac_sec_policy.proposed.type == 'fabric' - - nm_add_mac_sec_policy is changed - - nm_add_mac_sec_policy.current.name == 'ansible_mac_sec_policy' - - nm_add_mac_sec_policy.current.type == 'fabric' - - nm_add_mac_sec_policy.current.description == '' - - nm_add_mac_sec_policy.current.adminState == 'enabled' - - nm_add_mac_sec_policy.current.macsecParams.cipherSuite == '256GcmAesXpn' - - nm_add_mac_sec_policy.current.macsecParams.sakExpiryTime == 0 - - nm_add_mac_sec_policy.current.macsecParams.securityPol == 'shouldSecure' - - nm_add_mac_sec_policy.current.macsecParams.windowSize == 0 - - nm_add_mac_sec_policy.current.uuid is defined - - nm_add_mac_sec_policy_again is not changed - - nm_add_mac_sec_policy_again.previous.name == nm_add_mac_sec_policy_again.current.name == 'ansible_mac_sec_policy' - - nm_add_mac_sec_policy_again.previous.type == nm_add_mac_sec_policy_again.current.type == 'fabric' - - nm_add_mac_sec_policy_again.previous.description == nm_add_mac_sec_policy_again.current.description == '' - - nm_add_mac_sec_policy_again.previous.uuid is defined - - nm_add_mac_sec_policy_again.current.uuid is defined - - nm_add_mac_sec_policy_again.previous.macsecParams.cipherSuite == nm_add_mac_sec_policy_again.current.macsecParams.cipherSuite == '256GcmAesXpn' - - nm_add_mac_sec_policy_again.previous.macsecParams.sakExpiryTime == nm_add_mac_sec_policy_again.current.macsecParams.sakExpiryTime == 0 - - nm_add_mac_sec_policy_again.previous.macsecParams.securityPol == nm_add_mac_sec_policy_again.current.macsecParams.securityPol == 'shouldSecure' - - nm_add_mac_sec_policy_again.previous.macsecParams.windowSize == nm_add_mac_sec_policy_again.current.macsecParams.windowSize == 0 - - # MACsec policy interface_type access - - name: Create a MACSec policy of interface_type 'access' - cisco.mso.ndo_mac_sec_policy: &add_mac_sec_policy_access - <<: *mso_info - template: ansible_fabric_policy_template - mac_sec_policy: ansible_mac_sec_policy_access - interface_type: access - state: present - register: add_mac_sec_policy_access - - - name: Assert that the MACsec policy was created - assert: - that: - - add_mac_sec_policy_access is changed - - add_mac_sec_policy_access.previous == {} - - add_mac_sec_policy_access.current.name == 'ansible_mac_sec_policy_access' - - add_mac_sec_policy_access.current.type == 'access' - - add_mac_sec_policy_access.current.description == '' - - add_mac_sec_policy_access.current.adminState == 'enabled' - - add_mac_sec_policy_access.current.macsecParams.cipherSuite == '256GcmAesXpn' - - add_mac_sec_policy_access.current.macsecParams.sakExpiryTime == 0 - - add_mac_sec_policy_access.current.macsecParams.securityPol == 'shouldSecure' - - add_mac_sec_policy_access.current.macsecParams.windowSize == 64 - - add_mac_sec_policy_access.current.macsecParams.confOffSet == 'offset0' - - add_mac_sec_policy_access.current.macsecParams.keyServerPrio == 16 - - add_mac_sec_policy_access.current.uuid is defined - - # UPDATE - - # Only one mac_sec_keys can be added during creation - - name: Create another MACsec policy of interface_type 'access' - cisco.mso.ndo_mac_sec_policy: &add_mac_sec_policy_2 - <<: *mso_info - template: ansible_fabric_policy_template - mac_sec_policy: ansible_mac_sec_policy_2 - description: 'Ansible MACsec Policy description' - admin_state: 'enabled' - cipher_suite: '256_gcm_aes' - window_size: 105 - security_policy: 'should_secure' - sak_expiry_time: 99 - confidentiality_offset: 50 - key_server_priority: 11 - interface_type: access - mac_sec_keys: - - key_name: abc12 - psk: 'AA111111111111111111111111111111111111111111111111111111111111aa' - start_time: '2029-12-11 11:12:13' - end_time: '2030-12-11 11:12:13' - - key_name: ABC - psk: 'AAabcdabcdabcdabcdabcdabcdabcdab11111111111111111111111111111aaa' - state: present - register: nm_add_mac_sec_policy_2 - - - name: Update the MACsec policy of interface_type 'access' (check mode) - cisco.mso.ndo_mac_sec_policy: &update_mac_sec_policy - <<: *add_mac_sec_policy_2 - description: 'Updated description' - admin_state: 'disabled' - cipher_suite: '128_gcm_aes' - window_size: 110 - security_policy: 'must_secure' - sak_expiry_time: 100 - confidentiality_offset: 30 - key_server_priority: 10 - mac_sec_keys: - - key_name: abc12 - psk: 'AAabcdabcdabcdabcdabcdabcdabcdab' - start_time: '2029-12-11 11:12:13' - - key_name: ABC - psk: 'AAabcdabcdabcdabcdabcdabcdabcdab' - - key_name: aaa11 - psk: 'AAabcdabcdabcdabcdabcdabcdabcdab' - start_time: '2025-10-10 10:12:13' - end_time: '2026-10-10 10:12:13' - state: present - check_mode: true - register: cm_update_mac_sec_policy - - - name: Update the MACsec policy of interface_type 'access' - cisco.mso.ndo_mac_sec_policy: - <<: *update_mac_sec_policy - register: nm_update_mac_sec_policy - - # Idempotency for update cannot be checked as the psk is encrypted and the encrypted value changes every time - - name: Update MACsec policy of interface_type 'access' again - cisco.mso.ndo_mac_sec_policy: - <<: *update_mac_sec_policy - ignore_errors: true - register: nm_update_mac_sec_policy_again - - - name: Assert that the MACsec policy was updated - assert: - that: - - cm_update_mac_sec_policy is changed - - cm_update_mac_sec_policy.previous.description == 'Ansible MACsec Policy description' - - cm_update_mac_sec_policy.previous.adminState == 'enabled' - - cm_update_mac_sec_policy.previous.macsecParams.cipherSuite == '256GcmAes' - - cm_update_mac_sec_policy.previous.macsecParams.windowSize == 105 - - cm_update_mac_sec_policy.previous.macsecParams.securityPol == 'shouldSecure' - - cm_update_mac_sec_policy.previous.macsecParams.sakExpiryTime == 99 - - cm_update_mac_sec_policy.previous.macsecParams.confOffSet == 'offset50' - - cm_update_mac_sec_policy.previous.macsecParams.keyServerPrio == 11 - - cm_update_mac_sec_policy.current.description == cm_update_mac_sec_policy.proposed.description == 'Updated description' - - cm_update_mac_sec_policy.current.adminState == cm_update_mac_sec_policy.proposed.adminState == 'disabled' - - cm_update_mac_sec_policy.current.macsecParams.cipherSuite == cm_update_mac_sec_policy.proposed.macsecParams.cipherSuite == '128GcmAes' - - cm_update_mac_sec_policy.current.macsecParams.windowSize == cm_update_mac_sec_policy.proposed.macsecParams.windowSize == 110 - - cm_update_mac_sec_policy.current.macsecParams.securityPol == cm_update_mac_sec_policy.proposed.macsecParams.securityPol == 'mustSecure' - - cm_update_mac_sec_policy.current.macsecParams.sakExpiryTime == cm_update_mac_sec_policy.proposed.macsecParams.sakExpiryTime == 100 - - cm_update_mac_sec_policy.current.macsecParams.confOffSet == cm_update_mac_sec_policy.proposed.macsecParams.confOffSet == 'offset30' - - cm_update_mac_sec_policy.current.macsecParams.keyServerPrio == cm_update_mac_sec_policy.proposed.macsecParams.keyServerPrio == 10 - - nm_update_mac_sec_policy is changed - - nm_update_mac_sec_policy.previous.description == 'Ansible MACsec Policy description' - - nm_update_mac_sec_policy.previous.adminState == 'enabled' - - nm_update_mac_sec_policy.previous.macsecParams.cipherSuite == '256GcmAes' - - nm_update_mac_sec_policy.previous.macsecParams.windowSize == 105 - - nm_update_mac_sec_policy.previous.macsecParams.securityPol == 'shouldSecure' - - nm_update_mac_sec_policy.previous.macsecParams.sakExpiryTime == 99 - - nm_update_mac_sec_policy.previous.macsecParams.confOffSet == 'offset50' - - nm_update_mac_sec_policy.previous.macsecParams.keyServerPrio == 11 - - nm_update_mac_sec_policy.previous.macsecKeys | length == 2 - - nm_update_mac_sec_policy.current.description =='Updated description' - - nm_update_mac_sec_policy.current.adminState == 'disabled' - - nm_update_mac_sec_policy.current.macsecParams.cipherSuite == '128GcmAes' - - nm_update_mac_sec_policy.current.macsecParams.windowSize == 110 - - nm_update_mac_sec_policy.current.macsecParams.securityPol == 'mustSecure' - - nm_update_mac_sec_policy.current.macsecParams.sakExpiryTime == 100 - - nm_update_mac_sec_policy.current.macsecParams.confOffSet == 'offset30' - - nm_update_mac_sec_policy.current.macsecParams.keyServerPrio == 10 - - nm_update_mac_sec_policy.current.macsecKeys | length == 3 - - nm_update_mac_sec_policy.current.uuid is defined - - nm_update_mac_sec_policy_again is changed - - nm_update_mac_sec_policy_again.previous.name == cm_update_mac_sec_policy.current.name == nm_update_mac_sec_policy.current.name == 'ansible_mac_sec_policy_2' - - nm_update_mac_sec_policy_again.previous.type == cm_update_mac_sec_policy.current.type == nm_update_mac_sec_policy.current.type == 'access' - - nm_update_mac_sec_policy_again.previous.description == cm_update_mac_sec_policy.current.description == nm_update_mac_sec_policy.current.description == 'Updated description' - - nm_update_mac_sec_policy_again.current.uuid is defined - - nm_update_mac_sec_policy_again.previous.uuid is defined - - - name: Update the MACsec policy name - cisco.mso.ndo_mac_sec_policy: - <<: *mso_info - template: ansible_fabric_policy_template - mac_sec_policy_uuid: '{{ nm_add_mac_sec_policy_again.current.uuid }}' - mac_sec_policy: ansible_mac_sec_policy_changed - state: present - register: nm_update_mac_sec_policy_uuid - - - name: Assert that the MACsec policy name was updated - assert: - that: - - nm_update_mac_sec_policy_uuid is changed - - nm_update_mac_sec_policy_uuid.previous.name == 'ansible_mac_sec_policy' - - nm_update_mac_sec_policy_uuid.current.name == 'ansible_mac_sec_policy_changed' - - nm_update_mac_sec_policy_uuid.current.type == nm_update_mac_sec_policy_uuid.current.type == 'fabric' - - nm_update_mac_sec_policy_uuid.current.description == nm_update_mac_sec_policy_uuid.current.description == '' - - nm_update_mac_sec_policy_uuid.current.adminState == nm_update_mac_sec_policy_uuid.current.adminState == 'enabled' - - nm_update_mac_sec_policy_uuid.current.macsecParams.cipherSuite == nm_update_mac_sec_policy_uuid.current.macsecParams.cipherSuite == '256GcmAesXpn' - - nm_update_mac_sec_policy_uuid.current.macsecParams.sakExpiryTime == nm_update_mac_sec_policy_uuid.current.macsecParams.sakExpiryTime == 0 - - nm_update_mac_sec_policy_uuid.current.macsecParams.securityPol == nm_update_mac_sec_policy_uuid.current.macsecParams.securityPol == 'shouldSecure' - - nm_update_mac_sec_policy_uuid.current.macsecParams.windowSize == nm_update_mac_sec_policy_uuid.current.macsecParams.windowSize == 0 - - nm_update_mac_sec_policy_uuid.previous.uuid == nm_update_mac_sec_policy_uuid.current.uuid - - nm_update_mac_sec_policy_uuid.current.uuid is defined - - - name: Update the MACsec policy by removing the mac_sec_keys - cisco.mso.ndo_mac_sec_policy: - <<: *update_mac_sec_policy - mac_sec_keys: [] - state: present - register: rm_update_mac_sec_policy_key - - - name: Assert that the MACsec policy was updated by removing the mac_sec_keys - assert: - that: - - rm_update_mac_sec_policy_key is changed - - rm_update_mac_sec_policy_key.previous.macsecKeys | length == 3 - - rm_update_mac_sec_policy_key.current.macsecKeys is not defined - - # QUERY - - name: Query a MACsec policy with name - cisco.mso.ndo_mac_sec_policy: - <<: *mso_info - template: ansible_fabric_policy_template - mac_sec_policy: ansible_mac_sec_policy_changed - state: query - register: query_one - - - name: Query all MACsec policies in a template - cisco.mso.ndo_mac_sec_policy: - <<: *mso_info - template: ansible_fabric_policy_template - state: query - register: query_all - - - name: Assert that the MACsec policy was queried - assert: - that: - - query_one is not changed - - query_one.current.name == 'ansible_mac_sec_policy_changed' - - query_one.current.type == 'fabric' - - query_one.current.description == '' - - query_all is not changed - - query_all.current | length >= 2 - - - name: Query a MACsec policy with UUID - cisco.mso.ndo_mac_sec_policy: - <<: *mso_info - template: ansible_fabric_policy_template - mac_sec_policy_uuid: '{{ nm_update_mac_sec_policy_uuid.current.uuid }}' - state: query - register: query_uuid - - - name: Assert that the MACsec policy was queried with mac_sec_policy UUID - assert: - that: - - query_uuid is not changed - - query_uuid.current.name == 'ansible_mac_sec_policy_changed' - - query_uuid.current.type == 'fabric' - - query_uuid.current.description == '' - - # ERROR - - name: Update the interface_type of exisiting MACsec policy - cisco.mso.ndo_mac_sec_policy: - <<: *mso_info - template: ansible_fabric_policy_template - mac_sec_policy: ansible_mac_sec_policy_changed - interface_type: access - state: present - ignore_errors: true - register: nm_update_mac_sec_policy_type - - - name: Assert that the MACsec policy interface_type cannot be updated - assert: - that: - - nm_update_mac_sec_policy_type is failed - - nm_update_mac_sec_policy_type.msg == 'Type cannot be changed for an existing MACSec Policy.' - - - name: Validate MACsec policy with invalide time format - cisco.mso.ndo_mac_sec_policy: - <<: *mso_info - template: ansible_fabric_policy_template - mac_sec_policy: ansible_mac_sec_policy_2 - description: 'Ansible MACsec Policy description' - interface_type: access - mac_sec_keys: - - key_name: abc12 - psk: 'AA111111111111111111111111111111111111111111111111111111111111aa' - start_time: 'wrong-time 11:12:13' - end_time: '2030-12-11 11:12:13' - state: present - ignore_errors: true - register: validate_invalid_time - - - name: Assert that the MACsec policy interface_type cannot be updated - assert: - that: - - validate_invalid_time is failed - - validate_invalid_time.msg == "TIME FORMAT ERROR{{":"}} The time must be in 'YYYY-MM-DD HH:MM:SS' format." - - # DELETE - - name: Delete a MACsec policy with name (check mode) - cisco.mso.ndo_mac_sec_policy: &delete_mac_sec_policy - <<: *mso_info - template: ansible_fabric_policy_template - mac_sec_policy: ansible_mac_sec_policy_2 - state: absent - check_mode: true - register: cm_delete_mac_sec_policy - - - name: Delete a MACsec policy with name - cisco.mso.ndo_mac_sec_policy: - <<: *delete_mac_sec_policy - register: nm_delete_mac_sec_policy - - - name: Delete a MACsec policy with name again - cisco.mso.ndo_mac_sec_policy: - <<: *delete_mac_sec_policy - register: nm_delete_mac_sec_policy_again - - - name: Assert that the MACsec policy was deleted - assert: - that: - - cm_delete_mac_sec_policy is changed - - nm_delete_mac_sec_policy is changed - - nm_delete_mac_sec_policy_again is not changed - - nm_delete_mac_sec_policy.previous.name == 'ansible_mac_sec_policy_2' - - nm_delete_mac_sec_policy.current == {} - - nm_delete_mac_sec_policy_again.current == nm_delete_mac_sec_policy_again.previous == {} - - - name: Delete a MACSec policy with UUID - cisco.mso.ndo_mac_sec_policy: - <<: *mso_info - template: ansible_fabric_policy_template - mac_sec_policy_uuid: '{{ nm_update_mac_sec_policy_uuid.current.uuid }}' - state: absent - register: delete_mac_sec_policy_uuid - - - name: Assert that the MACsec policy was deleted using UUID - assert: - that: - - delete_mac_sec_policy_uuid is changed - - delete_mac_sec_policy_uuid.previous.name == 'ansible_mac_sec_policy_changed' - - delete_mac_sec_policy_uuid.current == {} - - # CLEANUP - - name: Remove fabric template - cisco.mso.ndo_template: - <<: *template_absent \ No newline at end of file diff --git a/tests/integration/targets/ndo_mac_sec_policy/aliases b/tests/integration/targets/ndo_macsec_policy/aliases similarity index 100% rename from tests/integration/targets/ndo_mac_sec_policy/aliases rename to tests/integration/targets/ndo_macsec_policy/aliases diff --git a/tests/integration/targets/ndo_macsec_policy/tasks/main.yml b/tests/integration/targets/ndo_macsec_policy/tasks/main.yml new file mode 100644 index 00000000..74dfe98f --- /dev/null +++ b/tests/integration/targets/ndo_macsec_policy/tasks/main.yml @@ -0,0 +1,402 @@ +# Test code for the MSO modules +# Copyright: (c) 2024, Anvitha Jain (@anvjain) + +# 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 MultiSite host, username and password + ansible.builtin.fail: + msg: 'Please define the following variables: mso_hostname, mso_username and mso_password.' + when: mso_hostname is not defined or mso_username is not defined or mso_password is not defined + +# CLEAN ENVIRONMENT +- name: Set vars + ansible.builtin.set_fact: + mso_info: &mso_info + host: '{{ mso_hostname }}' + username: '{{ mso_username }}' + password: '{{ mso_password }}' + validate_certs: '{{ mso_validate_certs | default(false) }}' + use_ssl: '{{ mso_use_ssl | default(true) }}' + use_proxy: '{{ mso_use_proxy | default(true) }}' + output_level: '{{ mso_output_level | default("debug") }}' + +# QUERY VERSION +- name: Query MSO version + cisco.mso.mso_version: + <<: *mso_info + state: query + register: version + + +- name: Execute tasks only for MSO version > 4.3 + when: version.current.version is version('4.3', '>=') + block: + - name: Remove fabric template + cisco.mso.ndo_template: &template_absent + <<: *mso_info + name: ansible_fabric_policy_template + type: fabric_policy + state: absent + + - name: Create a fabric template + cisco.mso.ndo_template: + <<: *template_absent + state: present + + # CREATE + + # MACsec policy interface_type fabric + - name: Create a MACsec policy of interface_type 'fabric' (check mode) + cisco.mso.ndo_macsec_policy: &add_macsec_policy + <<: *mso_info + template: ansible_fabric_policy_template + macsec_policy: ansible_macsec_policy + state: present + check_mode: true + register: cm_add_macsec_policy + + - name: Create a MACsec policy of interface_type 'fabric' + cisco.mso.ndo_macsec_policy: + <<: *add_macsec_policy + register: nm_add_macsec_policy + + - name: Create MACsec policy again + cisco.mso.ndo_macsec_policy: + <<: *add_macsec_policy + register: nm_add_macsec_policy_again + + - name: Assert that the MACsec policy was created + assert: + that: + - cm_add_macsec_policy is changed + - cm_add_macsec_policy.previous == nm_add_macsec_policy.previous == {} + - cm_add_macsec_policy.current.name == cm_add_macsec_policy.proposed.name == 'ansible_macsec_policy' + - cm_add_macsec_policy.current.type == cm_add_macsec_policy.proposed.type == 'fabric' + - nm_add_macsec_policy is changed + - nm_add_macsec_policy.current.name == 'ansible_macsec_policy' + - nm_add_macsec_policy.current.type == 'fabric' + - nm_add_macsec_policy.current.description == '' + - nm_add_macsec_policy.current.adminState == 'enabled' + - nm_add_macsec_policy.current.macsecParams.cipherSuite == '256GcmAesXpn' + - nm_add_macsec_policy.current.macsecParams.sakExpiryTime == 0 + - nm_add_macsec_policy.current.macsecParams.securityPol == 'shouldSecure' + - nm_add_macsec_policy.current.macsecParams.windowSize == 0 + - nm_add_macsec_policy.current.uuid is defined + - nm_add_macsec_policy_again is not changed + - nm_add_macsec_policy_again.previous.name == nm_add_macsec_policy_again.current.name == 'ansible_macsec_policy' + - nm_add_macsec_policy_again.previous.type == nm_add_macsec_policy_again.current.type == 'fabric' + - nm_add_macsec_policy_again.previous.description == nm_add_macsec_policy_again.current.description == '' + - nm_add_macsec_policy_again.previous.uuid is defined + - nm_add_macsec_policy_again.current.uuid is defined + - nm_add_macsec_policy_again.previous.macsecParams.cipherSuite == nm_add_macsec_policy_again.current.macsecParams.cipherSuite == '256GcmAesXpn' + - nm_add_macsec_policy_again.previous.macsecParams.sakExpiryTime == nm_add_macsec_policy_again.current.macsecParams.sakExpiryTime == 0 + - nm_add_macsec_policy_again.previous.macsecParams.securityPol == nm_add_macsec_policy_again.current.macsecParams.securityPol == 'shouldSecure' + - nm_add_macsec_policy_again.previous.macsecParams.windowSize == nm_add_macsec_policy_again.current.macsecParams.windowSize == 0 + + # MACsec policy interface_type access + - name: Create a MACSec policy of interface_type 'access' + cisco.mso.ndo_macsec_policy: &add_macsec_policy_access + <<: *mso_info + template: ansible_fabric_policy_template + macsec_policy: ansible_macsec_policy_access + interface_type: access + state: present + register: add_macsec_policy_access + + - name: Assert that the MACsec policy was created + assert: + that: + - add_macsec_policy_access is changed + - add_macsec_policy_access.previous == {} + - add_macsec_policy_access.current.name == 'ansible_macsec_policy_access' + - add_macsec_policy_access.current.type == 'access' + - add_macsec_policy_access.current.description == '' + - add_macsec_policy_access.current.adminState == 'enabled' + - add_macsec_policy_access.current.macsecParams.cipherSuite == '256GcmAesXpn' + - add_macsec_policy_access.current.macsecParams.sakExpiryTime == 0 + - add_macsec_policy_access.current.macsecParams.securityPol == 'shouldSecure' + - add_macsec_policy_access.current.macsecParams.windowSize == 64 + - add_macsec_policy_access.current.macsecParams.confOffSet == 'offset0' + - add_macsec_policy_access.current.macsecParams.keyServerPrio == 16 + - add_macsec_policy_access.current.uuid is defined + + # UPDATE + + # Only one macsec_keys can be added during creation + - name: Create another MACsec policy of interface_type 'access' + cisco.mso.ndo_macsec_policy: &add_macsec_policy_2 + <<: *mso_info + template: ansible_fabric_policy_template + macsec_policy: ansible_macsec_policy_2 + description: 'Ansible MACsec Policy description' + admin_state: 'enabled' + cipher_suite: '256_gcm_aes' + window_size: 105 + security_policy: 'should_secure' + sak_expiry_time: 99 + confidentiality_offset: 50 + key_server_priority: 11 + interface_type: access + macsec_keys: + - key_name: abc12 + psk: 'AA111111111111111111111111111111111111111111111111111111111111aa' + start_time: '2029-12-11 11:12:13' + end_time: '2030-12-11 11:12:13' + - key_name: ABC + psk: 'AAabcdabcdabcdabcdabcdabcdabcdab11111111111111111111111111111aaa' + state: present + register: nm_add_macsec_policy_2 + + - name: Update the MACsec policy of interface_type 'access' (check mode) + cisco.mso.ndo_macsec_policy: &update_macsec_policy + <<: *add_macsec_policy_2 + description: 'Updated description' + admin_state: 'disabled' + cipher_suite: '128_gcm_aes' + window_size: 110 + security_policy: 'must_secure' + sak_expiry_time: 100 + confidentiality_offset: 30 + key_server_priority: 10 + macsec_keys: + - key_name: abc12 + psk: 'AAabcdabcdabcdabcdabcdabcdabcdab' + start_time: '2029-12-11 11:12:13' + - key_name: ABC + psk: 'AAabcdabcdabcdabcdabcdabcdabcdab' + - key_name: aaa11 + psk: 'AAabcdabcdabcdabcdabcdabcdabcdab' + start_time: '2025-10-10 10:12:13' + end_time: '2026-10-10 10:12:13' + state: present + check_mode: true + register: cm_update_macsec_policy + + - name: Update the MACsec policy of interface_type 'access' + cisco.mso.ndo_macsec_policy: + <<: *update_macsec_policy + register: nm_update_macsec_policy + + # Idempotency for update cannot be checked as the psk is encrypted and the encrypted value changes every time + - name: Update MACsec policy of interface_type 'access' again + cisco.mso.ndo_macsec_policy: + <<: *update_macsec_policy + ignore_errors: true + register: nm_update_macsec_policy_again + + - name: Assert that the MACsec policy was updated + assert: + that: + - cm_update_macsec_policy is changed + - cm_update_macsec_policy.previous.description == 'Ansible MACsec Policy description' + - cm_update_macsec_policy.previous.adminState == 'enabled' + - cm_update_macsec_policy.previous.macsecParams.cipherSuite == '256GcmAes' + - cm_update_macsec_policy.previous.macsecParams.windowSize == 105 + - cm_update_macsec_policy.previous.macsecParams.securityPol == 'shouldSecure' + - cm_update_macsec_policy.previous.macsecParams.sakExpiryTime == 99 + - cm_update_macsec_policy.previous.macsecParams.confOffSet == 'offset50' + - cm_update_macsec_policy.previous.macsecParams.keyServerPrio == 11 + - cm_update_macsec_policy.current.description == cm_update_macsec_policy.proposed.description == 'Updated description' + - cm_update_macsec_policy.current.adminState == cm_update_macsec_policy.proposed.adminState == 'disabled' + - cm_update_macsec_policy.current.macsecParams.cipherSuite == cm_update_macsec_policy.proposed.macsecParams.cipherSuite == '128GcmAes' + - cm_update_macsec_policy.current.macsecParams.windowSize == cm_update_macsec_policy.proposed.macsecParams.windowSize == 110 + - cm_update_macsec_policy.current.macsecParams.securityPol == cm_update_macsec_policy.proposed.macsecParams.securityPol == 'mustSecure' + - cm_update_macsec_policy.current.macsecParams.sakExpiryTime == cm_update_macsec_policy.proposed.macsecParams.sakExpiryTime == 100 + - cm_update_macsec_policy.current.macsecParams.confOffSet == cm_update_macsec_policy.proposed.macsecParams.confOffSet == 'offset30' + - cm_update_macsec_policy.current.macsecParams.keyServerPrio == cm_update_macsec_policy.proposed.macsecParams.keyServerPrio == 10 + - nm_update_macsec_policy is changed + - nm_update_macsec_policy.previous.description == 'Ansible MACsec Policy description' + - nm_update_macsec_policy.previous.adminState == 'enabled' + - nm_update_macsec_policy.previous.macsecParams.cipherSuite == '256GcmAes' + - nm_update_macsec_policy.previous.macsecParams.windowSize == 105 + - nm_update_macsec_policy.previous.macsecParams.securityPol == 'shouldSecure' + - nm_update_macsec_policy.previous.macsecParams.sakExpiryTime == 99 + - nm_update_macsec_policy.previous.macsecParams.confOffSet == 'offset50' + - nm_update_macsec_policy.previous.macsecParams.keyServerPrio == 11 + - nm_update_macsec_policy.previous.macsecKeys | length == 2 + - nm_update_macsec_policy.current.description =='Updated description' + - nm_update_macsec_policy.current.adminState == 'disabled' + - nm_update_macsec_policy.current.macsecParams.cipherSuite == '128GcmAes' + - nm_update_macsec_policy.current.macsecParams.windowSize == 110 + - nm_update_macsec_policy.current.macsecParams.securityPol == 'mustSecure' + - nm_update_macsec_policy.current.macsecParams.sakExpiryTime == 100 + - nm_update_macsec_policy.current.macsecParams.confOffSet == 'offset30' + - nm_update_macsec_policy.current.macsecParams.keyServerPrio == 10 + - nm_update_macsec_policy.current.macsecKeys | length == 3 + - nm_update_macsec_policy.current.uuid is defined + - nm_update_macsec_policy_again is changed + - nm_update_macsec_policy_again.previous.name == cm_update_macsec_policy.current.name == nm_update_macsec_policy.current.name == 'ansible_macsec_policy_2' + - nm_update_macsec_policy_again.previous.type == cm_update_macsec_policy.current.type == nm_update_macsec_policy.current.type == 'access' + - nm_update_macsec_policy_again.previous.description == cm_update_macsec_policy.current.description == nm_update_macsec_policy.current.description == 'Updated description' + - nm_update_macsec_policy_again.current.uuid is defined + - nm_update_macsec_policy_again.previous.uuid is defined + + - name: Update the MACsec policy name + cisco.mso.ndo_macsec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + macsec_policy_uuid: '{{ nm_add_macsec_policy_again.current.uuid }}' + macsec_policy: ansible_macsec_policy_changed + state: present + register: nm_update_macsec_policy_uuid + + - name: Assert that the MACsec policy name was updated + assert: + that: + - nm_update_macsec_policy_uuid is changed + - nm_update_macsec_policy_uuid.previous.name == 'ansible_macsec_policy' + - nm_update_macsec_policy_uuid.current.name == 'ansible_macsec_policy_changed' + - nm_update_macsec_policy_uuid.current.type == nm_update_macsec_policy_uuid.current.type == 'fabric' + - nm_update_macsec_policy_uuid.current.description == nm_update_macsec_policy_uuid.current.description == '' + - nm_update_macsec_policy_uuid.current.adminState == nm_update_macsec_policy_uuid.current.adminState == 'enabled' + - nm_update_macsec_policy_uuid.current.macsecParams.cipherSuite == nm_update_macsec_policy_uuid.current.macsecParams.cipherSuite == '256GcmAesXpn' + - nm_update_macsec_policy_uuid.current.macsecParams.sakExpiryTime == nm_update_macsec_policy_uuid.current.macsecParams.sakExpiryTime == 0 + - nm_update_macsec_policy_uuid.current.macsecParams.securityPol == nm_update_macsec_policy_uuid.current.macsecParams.securityPol == 'shouldSecure' + - nm_update_macsec_policy_uuid.current.macsecParams.windowSize == nm_update_macsec_policy_uuid.current.macsecParams.windowSize == 0 + - nm_update_macsec_policy_uuid.previous.uuid == nm_update_macsec_policy_uuid.current.uuid + - nm_update_macsec_policy_uuid.current.uuid is defined + + - name: Update the MACsec policy by removing the macsec_keys + cisco.mso.ndo_macsec_policy: + <<: *update_macsec_policy + macsec_keys: [] + state: present + register: rm_update_macsec_policy_key + + - name: Assert that the MACsec policy was updated by removing the macsec_keys + assert: + that: + - rm_update_macsec_policy_key is changed + - rm_update_macsec_policy_key.previous.macsecKeys | length == 3 + - rm_update_macsec_policy_key.current.macsecKeys is not defined + + # QUERY + - name: Query a MACsec policy with name + cisco.mso.ndo_macsec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + macsec_policy: ansible_macsec_policy_changed + state: query + register: query_one + + - name: Query all MACsec policies in a template + cisco.mso.ndo_macsec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + state: query + register: query_all + + - name: Assert that the MACsec policy was queried + assert: + that: + - query_one is not changed + - query_one.current.name == 'ansible_macsec_policy_changed' + - query_one.current.type == 'fabric' + - query_one.current.description == '' + - query_all is not changed + - query_all.current | length >= 2 + + - name: Query a MACsec policy with UUID + cisco.mso.ndo_macsec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + macsec_policy_uuid: '{{ nm_update_macsec_policy_uuid.current.uuid }}' + state: query + register: query_uuid + + - name: Assert that the MACsec policy was queried with macsec_policy UUID + assert: + that: + - query_uuid is not changed + - query_uuid.current.name == 'ansible_macsec_policy_changed' + - query_uuid.current.type == 'fabric' + - query_uuid.current.description == '' + + # ERROR + - name: Update the interface_type of exisiting MACsec policy + cisco.mso.ndo_macsec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + macsec_policy: ansible_macsec_policy_changed + interface_type: access + state: present + ignore_errors: true + register: nm_update_macsec_policy_type + + - name: Assert that the MACsec policy interface_type cannot be updated + assert: + that: + - nm_update_macsec_policy_type is failed + - nm_update_macsec_policy_type.msg == 'Type cannot be changed for an existing MACSec Policy.' + + - name: Validate MACsec policy with invalide time format + cisco.mso.ndo_macsec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + macsec_policy: ansible_macsec_policy_2 + description: 'Ansible MACsec Policy description' + interface_type: access + macsec_keys: + - key_name: abc12 + psk: 'AA111111111111111111111111111111111111111111111111111111111111aa' + start_time: 'wrong-time 11:12:13' + end_time: '2030-12-11 11:12:13' + state: present + ignore_errors: true + register: validate_invalid_time + + - name: Assert that the MACsec policy interface_type cannot be updated + assert: + that: + - validate_invalid_time is failed + - validate_invalid_time.msg == "ERROR{{":"}} The time must be in 'YYYY-MM-DD HH:MM:SS' format." + + # DELETE + - name: Delete a MACsec policy with name (check mode) + cisco.mso.ndo_macsec_policy: &delete_macsec_policy + <<: *mso_info + template: ansible_fabric_policy_template + macsec_policy: ansible_macsec_policy_2 + state: absent + check_mode: true + register: cm_delete_macsec_policy + + - name: Delete a MACsec policy with name + cisco.mso.ndo_macsec_policy: + <<: *delete_macsec_policy + register: nm_delete_macsec_policy + + - name: Delete a MACsec policy with name again + cisco.mso.ndo_macsec_policy: + <<: *delete_macsec_policy + register: nm_delete_macsec_policy_again + + - name: Assert that the MACsec policy was deleted + assert: + that: + - cm_delete_macsec_policy is changed + - nm_delete_macsec_policy is changed + - nm_delete_macsec_policy_again is not changed + - nm_delete_macsec_policy.previous.name == 'ansible_macsec_policy_2' + - nm_delete_macsec_policy.current == {} + - nm_delete_macsec_policy_again.current == nm_delete_macsec_policy_again.previous == {} + + - name: Delete a MACSec policy with UUID + cisco.mso.ndo_macsec_policy: + <<: *mso_info + template: ansible_fabric_policy_template + macsec_policy_uuid: '{{ nm_update_macsec_policy_uuid.current.uuid }}' + state: absent + register: delete_macsec_policy_uuid + + - name: Assert that the MACsec policy was deleted using UUID + assert: + that: + - delete_macsec_policy_uuid is changed + - delete_macsec_policy_uuid.previous.name == 'ansible_macsec_policy_changed' + - delete_macsec_policy_uuid.current == {} + + # CLEANUP + - name: Remove fabric template + cisco.mso.ndo_template: + <<: *template_absent \ No newline at end of file