From e9274b837cdf0db0f568ba6f3b01692ec2baed0d Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Thu, 18 Apr 2024 12:55:01 -0400 Subject: [PATCH] [ignore] Modify Documentations for both Flow Rules modules. Modify logic behind updating subnets and nodes for both modules --- plugins/module_utils/nd.py | 10 + plugins/module_utils/ndi.py | 23 +-- plugins/modules/nd_flow_rules.py | 47 +++-- plugins/modules/nd_interface_flow_rules.py | 173 ++++++++++-------- .../nd_interface_flow_rules/tasks/main.yml | 99 ++++++---- 5 files changed, 202 insertions(+), 150 deletions(-) diff --git a/plugins/module_utils/nd.py b/plugins/module_utils/nd.py index 996df75..d4f0ebd 100644 --- a/plugins/module_utils/nd.py +++ b/plugins/module_utils/nd.py @@ -46,6 +46,16 @@ def sanitize_dict(dict_to_sanitize, keys=None, values=None, recursive=True, remo return result +def sanitize_list(list_to_sanitize, keys=None, values=None, list_recursive=True, dict_recursive=True, remove_none_values=True): + result = deepcopy(list_to_sanitize) + for index, item in enumerate(list_to_sanitize): + if isinstance(item, dict): + result[index] = sanitize_dict(item, keys, values, dict_recursive, remove_none_values) + elif isinstance(item, list) and list_recursive: + result[index] = sanitize_list(item, keys, values, list_recursive, dict_recursive, remove_none_values) + return result + + if PY3: def cmp(a, b): diff --git a/plugins/module_utils/ndi.py b/plugins/module_utils/ndi.py index 3ea0339..4c2304d 100644 --- a/plugins/module_utils/ndi.py +++ b/plugins/module_utils/ndi.py @@ -568,18 +568,13 @@ def create_structured_data(self, tree, file): f.close() def create_flow_rules_subnet_payload(self, subnets, existing_subnets): - subnets_to_add = [] subnets_to_update = [] - if isinstance(subnets, list): - existing_subnet_set = {item["subnet"] for item in existing_subnets} - all_subnet_set = sorted(existing_subnet_set.union(subnets)) - for subnet in all_subnet_set: - if subnet in subnets and subnet not in existing_subnet_set: - subnets_to_add.append({"subnet": subnet}) - subnets_to_update.append({"subnet": subnet, "operation": "ADD"}) - elif subnet not in subnets and subnet in existing_subnet_set: - subnet_id = next((existing_subnet["flowRuleAttributeUuid"] for existing_subnet in existing_subnets if existing_subnet["subnet"] == subnet)) - subnets_to_update.append({"subnet": subnet, "operation": "DELETE", "flowRuleAttributeUuid": subnet_id}) - else: - subnets_to_add.append({"subnet": subnet}) - return subnets_to_add, subnets_to_update + existing_subnet_set = {item["subnet"] for item in existing_subnets} + all_subnet_set = sorted(existing_subnet_set.union(subnets)) + for subnet in all_subnet_set: + if subnet in subnets and subnet not in existing_subnet_set: + subnets_to_update.append({"subnet": subnet, "operation": "ADD"}) + elif subnet not in subnets and subnet in existing_subnet_set: + subnet_id = next((existing_subnet["flowRuleAttributeUuid"] for existing_subnet in existing_subnets if existing_subnet["subnet"] == subnet)) + subnets_to_update.append({"subnet": subnet, "operation": "DELETE", "flowRuleAttributeUuid": subnet_id}) + return subnets_to_update diff --git a/plugins/modules/nd_flow_rules.py b/plugins/modules/nd_flow_rules.py index 57cd08b..6cec023 100644 --- a/plugins/modules/nd_flow_rules.py +++ b/plugins/modules/nd_flow_rules.py @@ -27,7 +27,7 @@ aliases: [ fab_name, ig_name ] site_name: description: - - The name of the ACI Fabric. + - The name of the Assurance Entity. type: str aliases: [ site ] flow_rule: @@ -38,13 +38,13 @@ tenant: description: - The name of an existing Tenant. - - Once the Flow Rule is created, this cannot be modified. + - The Flow Rule Tenant cannot be modified. type: str aliases: [ tenant_name ] vrf: description: - The name of an existing VRF under an existing I(tenant). - - Once the Flow Rule is created, This cannot be modified. + - The Flow Rule VRF cannot be modified. type: str aliases: [ vrf_name ] subnets: @@ -135,8 +135,8 @@ def main(): argument_spec = nd_argument_spec() argument_spec.update( - insights_group=dict(type="str", aliases=["fab_name", "ig_name"]), - site_name=dict(type="str", aliases=["site"]), + insights_group=dict(type="str",required=True, aliases=["fab_name", "ig_name"]), + site=dict(type="str",required=True, aliases=["site_name"]), flow_rule=dict(type="str", aliases=["flow_rule_name", "name"]), # Not required to query all objects tenant=dict(type="str", aliases=["tenant_name"]), vrf=dict(type="str", aliases=["vrf_name"]), @@ -148,9 +148,8 @@ def main(): argument_spec=argument_spec, supports_check_mode=False, required_if=[ - ["state", "present", ["insights_group", "site_name", "flow_rule"]], - ["state", "absent", ["insights_group", "site_name", "flow_rule"]], - ["state", "query", ["insights_group", "site_name"]], + ["state", "present", ["flow_rule"]], + ["state", "absent", ["flow_rule"]], ], ) @@ -159,7 +158,7 @@ def main(): state = nd.params.get("state") insights_group = nd.params.get("insights_group") - site_name = nd.params.get("site_name") + site = nd.params.get("site") flow_rule = nd.params.get("flow_rule") tenant = nd.params.get("tenant") vrf = nd.params.get("vrf") @@ -172,9 +171,8 @@ def main(): "fabricName", ] - trigger_path = ndi.config_ig_path + "/" + ndi.flow_rules_path.format(insights_group, site_name) - flow_rules_history = ndi.query_data(trigger_path) - nd.existing = {} + path = "{0}/{1}".format(ndi.config_ig_path, ndi.flow_rules_path.format(insights_group, site)) + flow_rules_history = ndi.query_data(path) uuid = None existing_subnets = [] for flow_rules_config in flow_rules_history: @@ -183,19 +181,18 @@ def main(): uuid = flow_rules_config.get("uuid") existing_subnets.extend(flow_rules_config.get("flowRuleAttributeList", [])) + if state == "present": nd.previous = nd.existing - flow_rule_config = {"name": flow_rule, "tenant": tenant, "vrf": vrf} - subnets_to_add, subnets_to_update = ndi.create_flow_rules_subnet_payload(subnets, existing_subnets) - flow_rule_config.update(flowRuleAttributeList=subnets_to_add) - if flow_rule_config != nd.previous: - method = "POST" - payload = {"flowRulesList": [flow_rule_config]} - if uuid: - method = "PUT" - trigger_path = "{0}/{1}".format(trigger_path, uuid) - payload = {"flowRuleAttributeList": subnets_to_update} - resp = nd.request(trigger_path, method=method, prefix=ndi.prefix, data=payload) + if uuid: + if isinstance(subnets, list) and [item["subnet"] for item in existing_subnets] != subnets: + payload = {"flowRuleAttributeList": ndi.create_flow_rules_subnet_payload(subnets, existing_subnets)} + resp = nd.request("{0}/{1}".format(path, uuid), method="PUT", prefix=ndi.prefix, data=payload) + nd.existing = sanitize_dict(resp.get("value", {}).get("data", [])[0], delete_keys) + else: + subnets_to_add = [{"subnet": subnet} for subnet in subnets] if isinstance(subnets, list) else [] + payload = {"flowRulesList": [{"name": flow_rule, "tenant": tenant, "vrf": vrf, "flowRuleAttributeList": subnets_to_add}]} + resp = nd.request(path, method="POST", prefix=ndi.prefix, data=payload) nd.existing = sanitize_dict(resp.get("value", {}).get("data", [])[0], delete_keys) elif state == "query": @@ -204,8 +201,8 @@ def main(): elif state == "absent": nd.previous = nd.existing - trigger_path = "{0}/{1}".format(trigger_path, uuid) - resp = nd.request(trigger_path, method="DELETE", prefix=ndi.prefix) + path = "{0}/{1}".format(path, uuid) + resp = nd.request(path, method="DELETE", prefix=ndi.prefix) nd.existing = {} nd.exit_json() diff --git a/plugins/modules/nd_interface_flow_rules.py b/plugins/modules/nd_interface_flow_rules.py index d4c8414..4b9e9f5 100644 --- a/plugins/modules/nd_interface_flow_rules.py +++ b/plugins/modules/nd_interface_flow_rules.py @@ -25,11 +25,11 @@ - The name of the insights group. type: str aliases: [ fab_name, ig_name ] - site_name: + site: description: - - The name of the ACI Fabric. + - The name of the Assurance Entity. type: str - aliases: [ site ] + aliases: [ site_name ] flow_rule: description: - The name of the Interface Flow Rule. @@ -44,8 +44,9 @@ aliases: [ type ] flow_rule_status: description: - - The state of the Interface Flow Rule. - - It can be C(enabled) or C(disabled) but while being C(disabled), The Interface Flow Rule cannot be modified. + - The status of the Interface Flow Rule. + - It can be C(enabled) or C(disabled). + - If C(disabled), The Interface Flow Rule cannot be modified or updated. type: str default: enabled choices: [ enabled, disabled ] @@ -61,10 +62,12 @@ description: - The node's ID. type: str + aliases: [ id ] node_name: description: - The name of the node. type: str + aliases: [ name ] tenant: description: - The name of the tenant. @@ -265,54 +268,61 @@ """ from ansible_collections.cisco.nd.plugins.module_utils.ndi import NDI -from ansible_collections.cisco.nd.plugins.module_utils.nd import NDModule, nd_argument_spec, sanitize_dict +from ansible_collections.cisco.nd.plugins.module_utils.nd import NDModule, nd_argument_spec, sanitize_dict, sanitize_list from ansible.module_utils.basic import AnsibleModule from ansible_collections.cisco.nd.plugins.module_utils.constants import INTERFACE_FLOW_RULES_TYPES_MAPPING, INTERFACE_FLOW_RULES_STATUS_MAPPING -import copy -def create_flow_rules_node_port(nodes=None, existing_nodes=None, flow_rule_type=None): - nodes_to_add = [] +def reformat_nodes_input(nodes=None, flow_rule_type=None): + nodes_formated = [] + for node in nodes: + node_formated = {"nodeId": node.get("node_id"), "nodeName": node.get("node_name")} + if flow_rule_type in ["L3_SUBIF", "SVI"]: + node_formated.update({"tenant": node.get("tenant"), "l3outName": node.get("l3out"), "encap": node.get("encap")}) + if flow_rule_type in ["PHYSICAL", "PORTCHANNEL", "L3_SUBIF"]: + ports_formated = [{"port": port} for port in node.get("ports", [])] + node_formated.update({"portsList": ports_formated}) + nodes_formated.append(node_formated) + return nodes_formated + + +def create_all_nodes_list(nodes_formated=None, existing_nodes=None): + nodes_formated_sanitized = sanitize_list(nodes_formated, ["portsList"]) + existing_nodes_sanitized = sanitize_list(existing_nodes, ["portsList", "flowNodeUuid"]) + set_nodes_formated = {tuple(sorted(node.items())) for node in nodes_formated_sanitized} + set_existing_nodes = {tuple(sorted(node.items())) for node in existing_nodes_sanitized} + all_nodes_set = sorted(set_existing_nodes.union(set_nodes_formated)) + all_nodes_list = [ dict(node) for node in all_nodes_set] + return nodes_formated_sanitized, existing_nodes_sanitized, all_nodes_list + + +def update_flow_rules_nodes_payload(nodes=None, existing_nodes=None, flow_rule_type=None): nodes_to_update = [] if isinstance(nodes, list): - existing_nodes_set = {item["nodeId"] for item in existing_nodes} - all_nodes_set = sorted(existing_nodes_set.union({item["node_id"] for item in nodes})) - for node_id in all_nodes_set: - check_node_input_id = any(node_input["node_id"] == node_id for node_input in nodes) - - if check_node_input_id and node_id not in existing_nodes_set: - node_input = next((node for node in nodes if node["node_id"] == node_id)) - node_input_formated = {"nodeId": node_input.get("node_id"), "nodeName": node_input.get("node_name")} - ports_input_formated = [{"port": port} for port in node_input.get("ports")] if node_input.get("ports") is not None else [] - if flow_rule_type in ["PHYSICAL", "PORTCHANNEL"]: - node_input_formated.update({"portsList": ports_input_formated}) - nodes_to_add.append(copy.deepcopy(node_input_formated)) - node_input_formated.update({"operation": "ADD"}) - nodes_to_update.append(node_input_formated) - else: - node_input_formated.update({"tenant": node_input.get("tenant"), "l3outName": node_input.get("l3out"), "encap": node_input.get("encap")}) - if flow_rule_type == "L3_SUBIF": - node_input_formated.update({"portsList": ports_input_formated}) - nodes_to_add.append(copy.deepcopy(node_input_formated)) - node_input_formated.update({"operation": "ADD"}) - nodes_to_update.append(node_input_formated) - - elif not check_node_input_id and node_id in existing_nodes_set: - node_to_delete = next((existing_node for existing_node in existing_nodes if existing_node["nodeId"] == node_id)) - node_to_delete.update({"operation": "DELETE"}) - nodes_to_update.append(node_to_delete) - - elif check_node_input_id and node_id in existing_nodes_set and flow_rule_type != "SVI": - ports_input = next((node.get("ports") for node in nodes if node["node_id"] == node_id)) - if isinstance(ports_input, list): - existing_node = next((existing_node for existing_node in existing_nodes if existing_node["nodeId"] == node_id)) - existing_ports, node_uuid = existing_node.get("portsList"), existing_node.get("flowNodeUuid") - existing_port_set = {item["port"] for item in existing_ports} + nodes_formated = reformat_nodes_input(nodes, flow_rule_type) + nodes_formated_sanitized, existing_nodes_sanitized, all_nodes_list = create_all_nodes_list(nodes_formated, existing_nodes) + for node in all_nodes_list: + if node in nodes_formated_sanitized and node not in existing_nodes_sanitized: + node_to_add = next(item for item in nodes_formated if sanitize_dict(item, ["portsList"]) == node) + node_to_add.update({"operation": "ADD"}) + nodes_to_update.append(node_to_add) + + elif node not in nodes_formated_sanitized and node in existing_nodes_sanitized: + node_to_remove = next(item for item in existing_nodes if sanitize_dict(item, ["portsList", "flowNodeUuid"]) == node) + node_to_remove.update({"operation": "DELETE"}) + nodes_to_update.append(node_to_remove) + + elif node in nodes_formated_sanitized and node in existing_nodes_sanitized and flow_rule_type !="SVI": + ports_input = next(item.get("portsList") for item in nodes_formated if sanitize_dict(item, ["portsList"]) == node) + ports_input = {item["port"] for item in ports_input} + existing_node = next(item for item in existing_nodes if sanitize_dict(item, ["portsList", "flowNodeUuid"]) == node) + existing_ports, node_uuid = existing_node.get("portsList"), existing_node.get("flowNodeUuid") + existing_port_set = {item["port"] for item in existing_ports} + if ports_input != existing_port_set: all_port_set = sorted(existing_port_set.union(ports_input)) for port in all_port_set: if port in ports_input and port not in existing_port_set: nodes_to_update.append({"flowNodeUuid": node_uuid, "operation": "MODIFY", "portsList": [{"port": port, "operation": "ADD"}]}) - nodes_to_add.append(dict(sanitize_dict(existing_node, ["flowNodeUuid", "portsList"]), **{"portsList": [{"port": port}]})) elif port not in ports_input and port in existing_port_set: port_uuid = next((existing_port["flowPortUuid"] for existing_port in existing_ports if existing_port["port"] == port)) nodes_to_update.append( @@ -322,24 +332,14 @@ def create_flow_rules_node_port(nodes=None, existing_nodes=None, flow_rule_type= "portsList": [{"flowPortUuid": port_uuid, "operation": "DELETE"}], } ) - else: - nodes_to_add.append(sanitize_dict(existing_node, ["flowNodeUuid", "flowPortUuid"])) - - else: - nodes_to_add.append( - sanitize_dict( - next((existing_node for existing_node in existing_nodes if existing_node["nodeId"] == node_id)), - ["flowNodeUuid", "flowPortUuid"], - ) - ) - return nodes_to_add, nodes_to_update + return nodes_to_update def main(): argument_spec = nd_argument_spec() argument_spec.update( - insights_group=dict(type="str", aliases=["fab_name", "ig_name"]), - site_name=dict(type="str", aliases=["site"]), + insights_group=dict(type="str", required=True, aliases=["fab_name", "ig_name"]), + site=dict(type="str",required=True, aliases=["site_name"]), flow_rule=dict(type="str", aliases=["interface_flow_rule", "flow_rule_name", "name"]), # Not required to query all objects flow_rule_status=dict(type="str", default="enabled", choices=["enabled", "disabled"], aliases=["status"]), flow_rule_type=dict(type="str", choices=["port_channel", "physical", "l3out_sub_interface", "l3out_svi"], aliases=["type"]), @@ -347,12 +347,12 @@ def main(): type="list", elements="dict", options=dict( - node_id=dict(type="str"), - node_name=dict(type="str"), + node_id=dict(type="str", required=True, aliases=["id"]), + node_name=dict(type="str", required=True, aliases=["name"]), tenant=dict(type="str"), l3out=dict(type="str"), encap=dict(type="str"), - ports=dict(type="list", elements="str"), + ports=dict(type="list", default=[], elements="str"), ), ), subnets=dict(type="list", elements="str"), @@ -363,9 +363,8 @@ def main(): argument_spec=argument_spec, supports_check_mode=False, required_if=[ - ["state", "present", ["insights_group", "site_name", "flow_rule"]], - ["state", "absent", ["insights_group", "site_name", "flow_rule"]], - ["state", "query", ["insights_group", "site_name"]], + ["state", "present", ["flow_rule"]], + ["state", "absent", ["flow_rule"]], ], ) @@ -374,7 +373,7 @@ def main(): state = nd.params.get("state") insights_group = nd.params.get("insights_group") - site_name = nd.params.get("site_name") + site = nd.params.get("site") flow_rule = nd.params.get("flow_rule") flow_rule_status = INTERFACE_FLOW_RULES_STATUS_MAPPING.get(nd.params.get("flow_rule_status")) flow_rule_type = INTERFACE_FLOW_RULES_TYPES_MAPPING.get(nd.params.get("flow_rule_type")) @@ -390,9 +389,8 @@ def main(): "flowPortUuid", ] - trigger_path = ndi.config_ig_path + "/" + ndi.interface_flow_rules_path.format(insights_group, site_name) - flow_rules_history = ndi.query_data(trigger_path) - nd.existing = {} + path = "{0}/{1}".format(ndi.config_ig_path, ndi.interface_flow_rules_path.format(insights_group, site)) + flow_rules_history = ndi.query_data(path) uuid = None existing_subnets = [] existing_nodes = [] @@ -405,18 +403,33 @@ def main(): if state == "present": nd.previous = nd.existing - flow_rule_config = {"name": flow_rule, "state": flow_rule_status, "type": flow_rule_type} - subnets_to_add, subnets_to_update = ndi.create_flow_rules_subnet_payload(subnets, existing_subnets) - nodes_to_add, nodes_to_update = create_flow_rules_node_port(nodes, existing_nodes, flow_rule_type) - flow_rule_config.update(flowRuleAttributeList=subnets_to_add, nodesList=nodes_to_add) - if flow_rule_config != nd.previous: - method = "POST" - payload = {"interfaceFlowRulesList": [flow_rule_config]} - if uuid: - method = "PUT" - trigger_path = "{0}/{1}".format(trigger_path, uuid) - payload = {"state": flow_rule_status, "nodesList": nodes_to_update, "flowRuleAttributeList": subnets_to_update} - resp = nd.request(trigger_path, method=method, prefix=ndi.prefix, data=payload) + if uuid: + payload = {} + nodes_to_update = update_flow_rules_nodes_payload(nodes, existing_nodes, flow_rule_type) + payload.update({"nodesList": nodes_to_update}) + if isinstance(subnets, list) and [item["subnet"] for item in existing_subnets] != subnets: + payload.update({"flowRuleAttributeList": ndi.create_flow_rules_subnet_payload(subnets, existing_subnets)}) + else: + payload.update({"flowRuleAttributeList": []}) + if nd.previous.get("state") != flow_rule_status: + payload.update({"state": flow_rule_status}) + if any(payload.get(k) for k in payload.keys()): + resp = nd.request("{0}/{1}".format(path, uuid), method="PUT", prefix=ndi.prefix, data=payload) + nd.existing = sanitize_dict(resp.get("value", {}).get("data", [])[0], delete_keys) + else: + nodes_to_add = [] + if isinstance(nodes, list): + for node in nodes: + node_to_add = {"nodeId": node.get("node_id"), "nodeName": node.get("node_name")} + if flow_rule_type in ["PHYSICAL", "PORTCHANNEL", "L3_SUBIF"]: + ports_to_add = [{"port": port} for port in node.get("ports", [])] + node_to_add.update({"portsList": ports_to_add}) + if flow_rule_type in ["SVI", "L3_SUBIF"]: + node_to_add.update({"tenant": node.get("tenant"), "l3outName": node.get("l3out"), "encap": node.get("encap")}) + nodes_to_add.append(node_to_add) + subnets_to_add = [{"subnet": subnet} for subnet in subnets] if isinstance(subnets, list) else [] + payload = {"interfaceFlowRulesList": [{"name": flow_rule, "state": flow_rule_status, "type": flow_rule_type, "nodesList": nodes_to_add, "flowRuleAttributeList": subnets_to_add}]} + resp = nd.request(path, method="POST", prefix=ndi.prefix, data=payload) nd.existing = sanitize_dict(resp.get("value", {}).get("data", [])[0], delete_keys) elif state == "query": @@ -429,8 +442,8 @@ def main(): elif state == "absent": nd.previous = nd.existing - trigger_path = "{0}/{1}".format(trigger_path, uuid) - resp = nd.request(trigger_path, method="DELETE", prefix=ndi.prefix) + path = "{0}/{1}".format(path, uuid) + resp = nd.request(path, method="DELETE", prefix=ndi.prefix) nd.existing = {} nd.exit_json() diff --git a/tests/integration/targets/nd_interface_flow_rules/tasks/main.yml b/tests/integration/targets/nd_interface_flow_rules/tasks/main.yml index 9f246ad..f563ba3 100644 --- a/tests/integration/targets/nd_interface_flow_rules/tasks/main.yml +++ b/tests/integration/targets/nd_interface_flow_rules/tasks/main.yml @@ -44,10 +44,6 @@ - eth1/2 - node_id: 1102 node_name: S1-LEAF1102 - ports: - - eth1/10 - - node_id: 1103 - node_name: S1-LEAF1103 register: nm_add_flow_rule_physical - name: Create Physical Interface Flow Rule - again @@ -56,7 +52,7 @@ register: nm_add_again_flow_rule_physical - name: Create L3Out Sub-Interface Interface Flow Rule - cisco.nd.nd_interface_flow_rules: + cisco.nd.nd_interface_flow_rules: &ndi_flow_rule_subif_present <<: *ndi_info <<: *ndi_site name: ansible_l3SubIf @@ -84,9 +80,9 @@ nodes: - node_id: 1102 node_name: S1-LEAF1102 - tenant: ansible_tenant - l3out: ansible_l3out - encap: ansible_vlan + tenant: ansible_tenant_1 + l3out: ansible_l3out_1 + encap: ansible_vlan_1 subnets: - 10.10.0.0/24 - 10.10.1.0/24 @@ -100,17 +96,14 @@ - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.type == "PHYSICAL" - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.state == "ENABLED" - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.flowRuleAttributeList == [] - - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.nodesList|length == 3 + - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.nodesList|length == 2 - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.nodesList.0.nodeId == "1101" - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.nodesList.0.nodeName == "S1-LEAF1101" - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.nodesList.0.portsList.0.port == "eth1/1" - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.nodesList.0.portsList.1.port == "eth1/2" - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.nodesList.1.nodeId == "1102" - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.nodesList.1.nodeName == "S1-LEAF1102" - - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.nodesList.1.portsList.0.port == "eth1/10" - - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.nodesList.2.nodeId == "1103" - - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.nodesList.2.nodeName == "S1-LEAF1103" - - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.nodesList.2.portsList == [] + - nm_add_flow_rule_physical.data.interfaceFlowRulesList.0.nodesList.1.portsList == [] - nm_add_flow_rule_physical.current == nm_add_flow_rule_physical.data.interfaceFlowRulesList.0 - nm_add_again_flow_rule_physical is not changed - nm_add_again_flow_rule_physical.current == nm_add_flow_rule_physical.current @@ -138,9 +131,9 @@ - nm_add_flow_rule_svi.data.interfaceFlowRulesList.0.nodesList|length == 1 - nm_add_flow_rule_svi.data.interfaceFlowRulesList.0.nodesList.0.nodeId == "1102" - nm_add_flow_rule_svi.data.interfaceFlowRulesList.0.nodesList.0.nodeName == "S1-LEAF1102" - - nm_add_flow_rule_svi.data.interfaceFlowRulesList.0.nodesList.0.tenant == "ansible_tenant" - - nm_add_flow_rule_svi.data.interfaceFlowRulesList.0.nodesList.0.l3outName == "ansible_l3out" - - nm_add_flow_rule_svi.data.interfaceFlowRulesList.0.nodesList.0.encap == "ansible_vlan" + - nm_add_flow_rule_svi.data.interfaceFlowRulesList.0.nodesList.0.tenant == "ansible_tenant_1" + - nm_add_flow_rule_svi.data.interfaceFlowRulesList.0.nodesList.0.l3outName == "ansible_l3out_1" + - nm_add_flow_rule_svi.data.interfaceFlowRulesList.0.nodesList.0.encap == "ansible_vlan_1" - nm_add_flow_rule_svi.current == nm_add_flow_rule_svi.data.interfaceFlowRulesList.0 # UPDATE TASKS @@ -156,7 +149,7 @@ - node_id: 1103 node_name: S1-LEAF1103 ports: - - eth1/20 + - eth1/1 register: nm_update_flow_rule_physical - name: Update current nodes for Physical Interface Flow Rule - again @@ -164,24 +157,40 @@ <<: *ndi_flow_rule_physical_update register: nm_update_again_flow_rule_physcial -- name: Update current subnets for L3Out SVI Interface Flow Rule +- name: Update current subnets and nodes for L3Out SVI Interface Flow Rule cisco.nd.nd_interface_flow_rules: &ndi_flow_rule_svi_update <<: *ndi_flow_rule_svi_present + nodes: + - node_id: 1102 + node_name: S1-LEAF1102 + tenant: ansible_tenant_1 + l3out: ansible_l3out_1 + encap: ansible_vlan_1 + - node_id: 1102 + node_name: S1-LEAF1102 + tenant: ansible_tenant_2 + l3out: ansible_l3out_2 + encap: ansible_vlan_2 subnets: - 10.10.1.0/24 - 10.10.2.0/24 register: nm_update_flow_rule_svi -- name: Update current subnets for L3Out SVI Interface Flow Rule - again +- name: Update current subnets and nodes for L3Out SVI Interface Flow Rule - again cisco.nd.nd_interface_flow_rules: <<: *ndi_flow_rule_svi_update register: nm_update_again_flow_rule_svi +- name: Disable L3Out Sub-Interface Interface Flow Rule + cisco.nd.nd_interface_flow_rules: + <<: *ndi_flow_rule_subif_present + status: disabled + register: nm_disable_flow_rule_l3SubIf + - name: Asserts for Interface Flow Rules update tasks ansible.builtin.assert: that: - nm_update_flow_rule_physical is changed - - nm_update_flow_rule_physical.data.flowRuleAttributeList == [] - nm_update_flow_rule_physical.data.nodesList.0.operation == "MODIFY" - nm_update_flow_rule_physical.data.nodesList.0.portsList.0.operation == "DELETE" - nm_update_flow_rule_physical.data.nodesList.1.operation == "MODIFY" @@ -190,9 +199,11 @@ - nm_update_flow_rule_physical.data.nodesList.2.operation == "DELETE" - nm_update_flow_rule_physical.data.nodesList.2.nodeId == "1102" - nm_update_flow_rule_physical.data.nodesList.2.nodeName == "S1-LEAF1102" - - nm_update_flow_rule_physical.data.nodesList.3.operation == "MODIFY" - - nm_update_flow_rule_physical.data.nodesList.3.portsList.0.operation == "ADD" - - nm_update_flow_rule_physical.data.nodesList.3.portsList.0.port == "eth1/20" + - nm_update_flow_rule_physical.data.nodesList.3.operation == "ADD" + - nm_update_flow_rule_physical.data.nodesList.3.nodeId == "1103" + - nm_update_flow_rule_physical.data.nodesList.3.nodeName == "S1-LEAF1103" + - nm_update_flow_rule_physical.data.nodesList.3.portsList|length == 1 + - nm_update_flow_rule_physical.data.nodesList.3.portsList.0.port == "eth1/1" - nm_update_flow_rule_physical.current.name == "ansible_phy" - nm_update_flow_rule_physical.current.type == "PHYSICAL" - nm_update_flow_rule_physical.current.state == "ENABLED" @@ -204,7 +215,7 @@ - nm_update_flow_rule_physical.current.nodesList.0.portsList.1.port == "eth1/3" - nm_update_flow_rule_physical.current.nodesList.1.nodeId == "1103" - nm_update_flow_rule_physical.current.nodesList.1.nodeName == "S1-LEAF1103" - - nm_update_flow_rule_physical.current.nodesList.1.portsList.0.port == "eth1/20" + - nm_update_flow_rule_physical.current.nodesList.1.portsList.0.port == "eth1/1" - nm_update_again_flow_rule_physcial is not changed - nm_update_again_flow_rule_physcial.current == nm_update_flow_rule_physical.current - nm_update_flow_rule_svi is changed @@ -212,20 +223,46 @@ - nm_update_flow_rule_svi.data.flowRuleAttributeList.0.operation == "DELETE" - nm_update_flow_rule_svi.data.flowRuleAttributeList.1.subnet == "10.10.2.0/24" - nm_update_flow_rule_svi.data.flowRuleAttributeList.1.operation == "ADD" + - nm_update_flow_rule_svi.data.nodesList|length == 1 + - nm_update_flow_rule_svi.data.nodesList.0.nodeId == "1102" + - nm_update_flow_rule_svi.data.nodesList.0.nodeName == "S1-LEAF1102" + - nm_update_flow_rule_svi.data.nodesList.0.tenant == "ansible_tenant_2" + - nm_update_flow_rule_svi.data.nodesList.0.l3outName == "ansible_l3out_2" + - nm_update_flow_rule_svi.data.nodesList.0.encap == "ansible_vlan_2" + - nm_update_flow_rule_svi.data.nodesList.0.operation == "ADD" - nm_update_flow_rule_svi.current.name == "ansible_svi" - nm_update_flow_rule_svi.current.type == "SVI" - nm_update_flow_rule_svi.current.state == "ENABLED" - nm_update_flow_rule_svi.current.flowRuleAttributeList|length == 2 - nm_update_flow_rule_svi.current.flowRuleAttributeList.0.subnet == "10.10.1.0/24" - nm_update_flow_rule_svi.current.flowRuleAttributeList.1.subnet == "10.10.2.0/24" - - nm_update_flow_rule_svi.current.nodesList|length == 1 + - nm_update_flow_rule_svi.current.nodesList|length == 2 - nm_update_flow_rule_svi.current.nodesList.0.nodeId == "1102" - nm_update_flow_rule_svi.current.nodesList.0.nodeName == "S1-LEAF1102" - - nm_update_flow_rule_svi.current.nodesList.0.tenant == "ansible_tenant" - - nm_update_flow_rule_svi.current.nodesList.0.l3outName == "ansible_l3out" - - nm_update_flow_rule_svi.current.nodesList.0.encap == "ansible_vlan" + - nm_update_flow_rule_svi.current.nodesList.0.tenant == "ansible_tenant_1" + - nm_update_flow_rule_svi.current.nodesList.0.l3outName == "ansible_l3out_1" + - nm_update_flow_rule_svi.current.nodesList.0.encap == "ansible_vlan_1" + - nm_update_flow_rule_svi.current.nodesList.1.nodeId == "1102" + - nm_update_flow_rule_svi.current.nodesList.1.nodeName == "S1-LEAF1102" + - nm_update_flow_rule_svi.current.nodesList.1.tenant == "ansible_tenant_2" + - nm_update_flow_rule_svi.current.nodesList.1.l3outName == "ansible_l3out_2" + - nm_update_flow_rule_svi.current.nodesList.1.encap == "ansible_vlan_2" - nm_update_again_flow_rule_svi is not changed - nm_update_again_flow_rule_svi.current == nm_update_flow_rule_svi.current + - nm_disable_flow_rule_l3SubIf is changed + - nm_disable_flow_rule_l3SubIf.data.state == "DISABLED" + - nm_disable_flow_rule_l3SubIf.current.name == "ansible_l3SubIf" + - nm_disable_flow_rule_l3SubIf.current.type == "L3_SUBIF" + - nm_disable_flow_rule_l3SubIf.current.state == "DISABLED" + - nm_disable_flow_rule_l3SubIf.current.flowRuleAttributeList == [] + - nm_disable_flow_rule_l3SubIf.current.nodesList|length == 1 + - nm_disable_flow_rule_l3SubIf.current.nodesList.0.nodeId == "1101" + - nm_disable_flow_rule_l3SubIf.current.nodesList.0.nodeName == "S1-LEAF1101" + - nm_disable_flow_rule_l3SubIf.current.nodesList.0.tenant == "ndi-demo" + - nm_disable_flow_rule_l3SubIf.current.nodesList.0.l3outName == "core_l3out" + - nm_disable_flow_rule_l3SubIf.current.nodesList.0.encap == "vlan-31" + - nm_disable_flow_rule_l3SubIf.current.nodesList|length == 1 + - nm_disable_flow_rule_l3SubIf.current.nodesList.0.portsList.0.port == "eth1/25" # QUERY TASKS - name: Query all Interface Flow Rules @@ -255,9 +292,9 @@ ansible.builtin.assert: that: - query_all_interface_flow_rules is not changed - - query_all_interface_flow_rules.current|length == 3 + - query_all_interface_flow_rules.current|length >= 3 - query_all_physical_flow_rules is not changed - - query_all_physical_flow_rules.current|length == 1 + - query_all_physical_flow_rules.current|length >= 1 - query_all_physical_flow_rules.current.0.type == "PHYSICAL" - query_specific_physical_flow_rules is not changed - query_specific_physical_flow_rules.current.name == "ansible_phy" @@ -271,7 +308,7 @@ - query_specific_physical_flow_rules.current.nodesList.0.portsList.1.port == "eth1/3" - query_specific_physical_flow_rules.current.nodesList.1.nodeId == "1103" - query_specific_physical_flow_rules.current.nodesList.1.nodeName == "S1-LEAF1103" - - query_specific_physical_flow_rules.current.nodesList.1.portsList.0.port == "eth1/20" + - query_specific_physical_flow_rules.current.nodesList.1.portsList.0.port == "eth1/1" # DELETION TASKS - name: Delete Physical Interface Flow Rule