diff --git a/aci-preupgrade-validation-script.py b/aci-preupgrade-validation-script.py index 62e26ea..908399e 100644 --- a/aci-preupgrade-validation-script.py +++ b/aci-preupgrade-validation-script.py @@ -4224,6 +4224,40 @@ def validate_32_64_bit_image_check(index, total_checks, tversion, **kwargs): return result +def out_of_service_ports_check(index, total_checks, **kwargs): + title = 'Out-of-Service Ports Check' + result = NA + msg = '' + headers = [ "Pod ID", "Node ID", "Port ID", "State from Switch" ] + data = [] + recommended_action = 'Review the list of Disabled Ports by GUI but enabled by CLI, to make sure these do not get disabled upon upgrade' + doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#out_of_service_ports_check' + print_title(title, index, total_checks) + oospath_regex = r'uni/fabric/outofsvc/rsoosPath-\[topology/pod-(?P[^/]+)/paths-(?P\d{3,4})/pathep-\[eth(?P.+)\]\]' + ##Validate fabricRsOosPath.dn against ethpmPhysIf.dn and ethpmPhysIf.operSt + oospath_api = 'fabricRsOosPath.json' + oospath = icurl('class', oospath_api) + oosports = [] + for oosint in oospath: + oosint_array = re.search(oospath_regex, oosint['fabricRsOosPath']['attributes']['dn']) + oosports.append([oosint_array.group("pod"), oosint_array.group("node"), oosint_array.group("int")]) + if oosports: + result = PASS + ethpmphysif_api = 'ethpmPhysIf.json' + ethpmphysif = icurl('class', ethpmphysif_api) + for pod, node, interface in oosports: + port_dn = 'topology/pod-'+ pod +'/node-'+ node+'/sys/phys-[eth'+interface+']/phys' + for ethpmport in ethpmphysif: + if ethpmport['ethpmPhysIf']['attributes']['dn'] == port_dn and ethpmport['ethpmPhysIf']['attributes']['operSt'] == 'up': + data.append([pod, node, interface, ethpmport['ethpmPhysIf']['attributes']['operSt']]) + else: + continue + if data: + result = FAIL_O + + print_result(title, result, msg, headers, data, recommended_action=recommended_action, doc_url=doc_url) + return result + if __name__ == "__main__": prints(' ==== %s%s, Script Version %s ====\n' % (ts, tz, SCRIPT_VERSION)) prints('!!!! Check https://github.com/datacenter/ACI-Pre-Upgrade-Validation-Script for Latest Release !!!!\n') @@ -4304,6 +4338,7 @@ def validate_32_64_bit_image_check(index, total_checks, tversion, **kwargs): eecdh_cipher_check, subnet_scope_check, unsupported_fec_configuration_ex_check, + out_of_service_ports_check, # Bugs ep_announce_check, @@ -4323,7 +4358,6 @@ def validate_32_64_bit_image_check(index, total_checks, tversion, **kwargs): lldp_custom_int_description_defect_check, rtmap_comm_match_defect_check, static_route_overlap_check - ] summary = {PASS: 0, FAIL_O: 0, FAIL_UF: 0, ERROR: 0, MANUAL: 0, POST: 0, NA: 0, 'TOTAL': len(checks)} for idx, check in enumerate(checks): diff --git a/docs/docs/validations.md b/docs/docs/validations.md index b5266a9..94497f4 100644 --- a/docs/docs/validations.md +++ b/docs/docs/validations.md @@ -118,7 +118,7 @@ Items | Faults | This Script [EECDH SSL Cipher Disabled][c14] | :white_check_mark: | :no_entry_sign: | :no_entry_sign: [BD and EPG Subnet must have matching scopes][c15] | :white_check_mark: | :no_entry_sign: | :no_entry_sign: [Unsupported FEC Configuration for N9K-C93180YC-EX][c16] | :white_check_mark: | :no_entry_sign: | :no_entry_sign: - +[Out-of-Service Ports check][c17] | :white_check_mark: | :no_entry_sign: | :no_entry_sign: [c1]: #vpc-paired-leaf-switches [c2]: #overlapping-vlan-pool @@ -136,6 +136,7 @@ Items | Faults | This Script [c14]: #eecdh-ssl-cipher [c15]: #bd-and-epg-subnet-must-have-matching-scopes [c16]: #unsupported-fec-configuration-for-n9k-c93180yc-ex +[c17]: #out_of_service_ports_check ### Defect Condition Checks @@ -1930,6 +1931,15 @@ It is important to remove any unsupported configuration prior to ugprade to avoi fcotChannelNumber : Channel32 fecMode : ieee-rs-fec <<< ``` +### Out-of-Service Ports Check + +Any Port that has been disabled in the in the APIC GUI gets a corresponding fabricRsOosPath object which are listed under the "Disabled Interfaces and Decommissioned Switches" view. +A user can enable the ports manually by going to the switch CLI : configure terminal -> interface eth1/X -> no shutdown +This overwrites the APIC policy, but the fabricRsOosPath objects created to disable the interfaces will remain in the configuration policy. + +When an upgrade for a switch is triggered, the APICs will push the policies to the switches again. This will push the fabricRsOosPath policy to the switch again, thus disabling the ports- + +To avoid this, make sure no fabricRsOosPath Ports is enabled from the switch before the upgrade. ## Defect Check Details diff --git a/tests/out_of_service_ports_check/ethpmPhysIf-neg.json b/tests/out_of_service_ports_check/ethpmPhysIf-neg.json new file mode 100644 index 0000000..5998421 --- /dev/null +++ b/tests/out_of_service_ports_check/ethpmPhysIf-neg.json @@ -0,0 +1,22 @@ +[ + { + "ethpmPhysIf": { + "attributes": { + "dn": "topology/pod-1/node-103/sys/phys-[eth1/27]/phys", + "operSpeed": "10G", + "operSt": "down", + "usage": "blacklist,epg" + } + } + }, + { + "ethpmPhysIf": { + "attributes": { + "dn": "topology/pod-1/node-105/sys/phys-[eth1/27]/phys", + "operSpeed": "10G", + "operSt": "down", + "usage": "blacklist,epg" + } + } + } +] \ No newline at end of file diff --git a/tests/out_of_service_ports_check/ethpmPhysIf-pos.json b/tests/out_of_service_ports_check/ethpmPhysIf-pos.json new file mode 100644 index 0000000..36e4475 --- /dev/null +++ b/tests/out_of_service_ports_check/ethpmPhysIf-pos.json @@ -0,0 +1,22 @@ +[ + { + "ethpmPhysIf": { + "attributes": { + "dn": "topology/pod-1/node-103/sys/phys-[eth1/27]/phys", + "operSpeed": "10G", + "operSt": "up", + "usage": "blacklist,epg" + } + } + }, + { + "ethpmPhysIf": { + "attributes": { + "dn": "topology/pod-1/node-105/sys/phys-[eth1/27]/phys", + "operSpeed": "10G", + "operSt": "down", + "usage": "blacklist,epg" + } + } + } +] \ No newline at end of file diff --git a/tests/out_of_service_ports_check/fabricRsOosPath-neg.json b/tests/out_of_service_ports_check/fabricRsOosPath-neg.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/tests/out_of_service_ports_check/fabricRsOosPath-neg.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/out_of_service_ports_check/fabricRsOosPath-pos.json b/tests/out_of_service_ports_check/fabricRsOosPath-pos.json new file mode 100644 index 0000000..7a0e6f2 --- /dev/null +++ b/tests/out_of_service_ports_check/fabricRsOosPath-pos.json @@ -0,0 +1,32 @@ +[ + { + "fabricRsOosPath": + { + "attributes": + { + "dn": "uni/fabric/outofsvc/rsoosPath-[topology/pod-1/paths-103/pathep-[eth1/27]]", + "lc": "blacklist", + "monPolDn": "uni/fabric/monfab-default", + "rType": "mo", + "state": "unformed", + "tCl": "fabricPathEp", + "tDn": "topology/pod-1/paths-103/pathep-[eth1/27]" + } + } + }, + { + "fabricRsOosPath": + { + "attributes": + { + "dn": "uni/fabric/outofsvc/rsoosPath-[topology/pod-1/paths-105/pathep-[eth1/27]]", + "lc": "blacklist", + "monPolDn": "uni/fabric/monfab-default", + "rType": "mo", + "state": "unformed", + "tCl": "fabricPathEp", + "tDn": "topology/pod-1/paths-105/pathep-[eth1/27]" + } + } + } +] \ No newline at end of file diff --git a/tests/out_of_service_ports_check/test_out_of_service_ports_check.py b/tests/out_of_service_ports_check/test_out_of_service_ports_check.py new file mode 100644 index 0000000..01a078d --- /dev/null +++ b/tests/out_of_service_ports_check/test_out_of_service_ports_check.py @@ -0,0 +1,49 @@ +import os +import pytest +import logging +import importlib +from helpers.utils import read_data + +script = importlib.import_module("aci-preupgrade-validation-script") + +log = logging.getLogger(__name__) +dir = os.path.dirname(os.path.abspath(__file__)) + + +# icurl queries +oospath_api = 'fabricRsOosPath.json' +ethpmphysif_api = 'ethpmPhysIf.json' + + +@pytest.mark.parametrize( + "icurl_outputs, expected_result", + [ + ( + ## NO DISABLED PORTS , ETHPMPHYSIF PORTS all ports DOWN, RESULT = N/A + {oospath_api: read_data(dir, "fabricRsOosPath-neg.json"), + ethpmphysif_api: read_data(dir, "ethpmPhysIf-neg.json")}, + script.NA, + ), + ( + ## NO DISABLED PORTS , ETHPMPHYSIF PORTS with one UP, one DOWN, RESULT = N/A + {oospath_api: read_data(dir, "fabricRsOosPath-neg.json"), + ethpmphysif_api: read_data(dir, "ethpmPhysIf-pos.json")}, + script.NA, + ), + ( + ## TWO DISABLED PORTS , ETHPMPHYSIF PORTS with one UP, one DOWN, RESULT = UPGRADE FAILS + {oospath_api: read_data(dir, "fabricRsOosPath-pos.json"), + ethpmphysif_api: read_data(dir, "ethpmPhysIf-pos.json")}, + script.FAIL_O, + ), + ( + ## TWO DISABLED PORTS , ETHPMPHYSIF PORTS all ports DOWN, RESULT = PASS + {oospath_api: read_data(dir, "fabricRsOosPath-pos.json"), + ethpmphysif_api: read_data(dir, "ethpmPhysIf-neg.json")}, + script.PASS, + ) + ], +) +def test_logic(mock_icurl, expected_result): + result = script.out_of_service_ports_check(1, 1) + assert result == expected_result