diff --git a/aci-preupgrade-validation-script.py b/aci-preupgrade-validation-script.py index 5e79f56..a88f510 100644 --- a/aci-preupgrade-validation-script.py +++ b/aci-preupgrade-validation-script.py @@ -22,7 +22,7 @@ from textwrap import TextWrapper from getpass import getpass from collections import defaultdict, OrderedDict -from datetime import datetime +from datetime import datetime, timedelta from argparse import ArgumentParser from itertools import chain import threading @@ -6702,6 +6702,65 @@ def stale_dbgacEpgSummaryTask_check(tversion, **kwargs): return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) +@check_wrapper(check_title="InfraVLAN Overlap in Access Policy VLAN Pools") +def infravlan_overlap_access_policy_check(tversion, **kwargs): + result = PASS + headers = ["InfraVLAN", "VLAN Pool", "Encap Block"] + data = [] + recommended_action = "Remove InfraVLAN from VLAN pool block highligted or upgrade to fix version" + + doc_url = "https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#infravlan-overlap-access-policy-check" + + if not tversion: + return Result(result=MANUAL, msg=TVER_MISSING) + + if not (tversion.same_as("6.2(1g)") or ( + not tversion.older_than("6.1(3f)") and not tversion.newer_than("6.1(5e)") + )): + return Result(result=NA, msg=VER_NOT_AFFECTED) + + infra_vlan = None + lldpInsts = icurl('class', 'lldpInst.json?query-target-filter=wcard(lldpInst.dn,"/node-1/")') + for lldpInst in lldpInsts: + infra_vlan_id = lldpInst.get('lldpInst', {}).get('attributes', {}).get('infraVlan') + if not infra_vlan_id: + continue + match = re.search(r'\d+', str(infra_vlan_id)) + if match: + infra_vlan = int(match.group(0)) + break + + if infra_vlan is None: + return Result(result=ERROR, msg="Unable to determine InfraVLAN from lldpInst.") + + encap_blocks = icurl('class', 'fvnsEncapBlk.json') + dn_pool_re = re.compile(r'vlanns-\[(?P[^\]]+)\]') + for obj in encap_blocks: + blk_attr = obj.get('fvnsEncapBlk', {}).get('attributes', {}) + dn = blk_attr.get('dn', '') + from_encap = blk_attr.get('from') + to_encap = blk_attr.get('to') + if not dn or not from_encap or not to_encap: + continue + + pool_match = dn_pool_re.search(dn) + pool_name = pool_match.group('vlan_pool') if pool_match else '-' + + try: + from_vlan = int(str(from_encap).split('-')[-1]) + to_vlan = int(str(to_encap).split('-')[-1]) + except (ValueError, TypeError): + continue + + if min(from_vlan, to_vlan) <= infra_vlan <= max(from_vlan, to_vlan): + data.append([str(infra_vlan), pool_name, "{} to {}".format(from_encap, to_encap)]) + + if data: + result = FAIL_UF + + return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url) + + # ---- Script Execution ---- @@ -6877,7 +6936,8 @@ class CheckManager: wred_affected_model_check, n9k_c93180yc_fx3_switch_memory_check, stale_dbgacEpgSummaryTask_check, - + infravlan_overlap_access_policy_check, + ] ssh_checks = [ # General diff --git a/docs/docs/validations.md b/docs/docs/validations.md index f788681..b455b62 100644 --- a/docs/docs/validations.md +++ b/docs/docs/validations.md @@ -206,6 +206,7 @@ Items | Defect | This Script [WRED with Affected FM Models][d35] | CSCwt50713 | :white_check_mark: | :no_entry_sign: [N9K-C93180YC-FX3 Switch Memory Less Than 32GB][d36] | CSCwm42741 | :white_check_mark: | :no_entry_sign: [Stale dbgacEpgSummaryTask Objects][d37] | CSCwt69100 | :white_check_mark: | :no_entry_sign: +[InfraVLAN Overlap in Access Policy VLAN Pools][d38] | CSCwt58626 | :white_check_mark: | :no_entry_sign: [d1]: #ep-announce-compatibility [d2]: #eventmgr-db-size-defect-susceptibility @@ -244,6 +245,7 @@ Items | Defect | This Script [d35]: #wred-with-affected-fm-models [d36]: #n9k-c93180yc-fx3-switch-memory-less-than-32gb [d37]: #stale-dbgacepgsummarytask-objects +[d38]: #infravlan-overlap-access-policy-check ## General Check Details @@ -2846,6 +2848,13 @@ Affected versions: 6.1(5e) and below, or 6.2(1g). Contact Cisco TAC for next steps. For more details, refer to the workaround in [CSCwt69100][75]. +### Infravlan Overlap Access Policy Check + +Due to [CSCwt58626][77], when targeting APIC version 6.1(3f) to 6.1(5e) or 6.2(1g) , if the InfraVLAN overlaps with any user-configured VLAN pool range in Access Policies, fault `F4701` is raised for VLAN pools that include the InfraVLAN. After the upgrade, domains linked to those VLAN pools cannot be associated with new EPGs, although existing EPGs continue to function. + +To avoid this issue, modify user VLAN pool ranges so that the InfraVLAN does not overlap with any configured block or select target fixed version 6.2(2e) or above. + + [0]: https://github.com/datacenter/ACI-Pre-Upgrade-Validation-Script [1]: https://www.cisco.com/c/dam/en/us/td/docs/Website/datacenter/apicmatrix/index.html [2]: https://www.cisco.com/c/en/us/support/switches/nexus-9000-series-switches/products-release-notes-list.html @@ -2923,4 +2932,4 @@ Contact Cisco TAC for next steps. For more details, refer to the workaround in [ [74]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwm42741 [75]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt69100 [76]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt38698 - +[77]: https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwt58626 \ No newline at end of file diff --git a/tests/checks/infravlan_overlap_access_policy_check/fvnsEncapBlk_empty.json b/tests/checks/infravlan_overlap_access_policy_check/fvnsEncapBlk_empty.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/tests/checks/infravlan_overlap_access_policy_check/fvnsEncapBlk_empty.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/checks/infravlan_overlap_access_policy_check/fvnsEncapBlk_no_overlap.json b/tests/checks/infravlan_overlap_access_policy_check/fvnsEncapBlk_no_overlap.json new file mode 100644 index 0000000..823d709 --- /dev/null +++ b/tests/checks/infravlan_overlap_access_policy_check/fvnsEncapBlk_no_overlap.json @@ -0,0 +1,20 @@ +[ + { + "fvnsEncapBlk": { + "attributes": { + "from": "vlan-200", + "to": "vlan-300", + "dn": "uni/infra/vlanns-[vlan_pool1]-static/from-[vlan-200]-to-[vlan-300]" + } + } + }, + { + "fvnsEncapBlk": { + "attributes": { + "from": "vlan-500", + "to": "vlan-1000", + "dn": "uni/infra/vlanns-[vlan_pool2]-static/from-[vlan-500]-to-[vlan-1000]" + } + } + } +] diff --git a/tests/checks/infravlan_overlap_access_policy_check/fvnsEncapBlk_overlap_multiple_vlan_pool.json b/tests/checks/infravlan_overlap_access_policy_check/fvnsEncapBlk_overlap_multiple_vlan_pool.json new file mode 100644 index 0000000..c6fdd17 --- /dev/null +++ b/tests/checks/infravlan_overlap_access_policy_check/fvnsEncapBlk_overlap_multiple_vlan_pool.json @@ -0,0 +1,20 @@ +[ + { + "fvnsEncapBlk": { + "attributes": { + "from": "vlan-100", + "to": "vlan-4094", + "dn": "uni/infra/vlanns-[vlan_pool1]-static/from-[vlan-100]-to-[vlan-4094]" + } + } + }, + { + "fvnsEncapBlk": { + "attributes": { + "from": "vlan-4000", + "to": "vlan-4094", + "dn": "uni/infra/vlanns-[vlan_pool2]-static/from-[vlan-4000]-to-[vlan-4094]" + } + } + } +] diff --git a/tests/checks/infravlan_overlap_access_policy_check/fvnsEncapBlk_overlap_single_vlan_pool.json b/tests/checks/infravlan_overlap_access_policy_check/fvnsEncapBlk_overlap_single_vlan_pool.json new file mode 100644 index 0000000..beedded --- /dev/null +++ b/tests/checks/infravlan_overlap_access_policy_check/fvnsEncapBlk_overlap_single_vlan_pool.json @@ -0,0 +1,11 @@ +[ + { + "fvnsEncapBlk": { + "attributes": { + "from": "vlan-100", + "to": "vlan-4094", + "dn": "uni/infra/vlanns-[vlan_pool]-static/from-[vlan-100]-to-[vlan-4094]" + } + } + } +] diff --git a/tests/checks/infravlan_overlap_access_policy_check/lldpInst_empty.json b/tests/checks/infravlan_overlap_access_policy_check/lldpInst_empty.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/tests/checks/infravlan_overlap_access_policy_check/lldpInst_empty.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/checks/infravlan_overlap_access_policy_check/lldpInst_infra_vlan_multiple_entry.json b/tests/checks/infravlan_overlap_access_policy_check/lldpInst_infra_vlan_multiple_entry.json new file mode 100644 index 0000000..6a1bb8f --- /dev/null +++ b/tests/checks/infravlan_overlap_access_policy_check/lldpInst_infra_vlan_multiple_entry.json @@ -0,0 +1,102 @@ +[ + { + "lldpInst": { + "attributes": { + "adminSt": "enabled", + "dn": "topology/pod-1/node-201/sys/lldp/inst", + "infraVlan": "vlan-4093", + "sysDesc": "topology/pod-1/node-201" + } + } + }, + { + "lldpInst": { + "attributes": { + "adminSt": "enabled", + "dn": "topology/pod-1/node-202/sys/lldp/inst", + "infraVlan": "vlan-4093", + "sysDesc": "topology/pod-1/node-202" + } + } + }, + { + "lldpInst": { + "attributes": { + "adminSt": "enabled", + "dn": "topology/pod-1/node-203/sys/lldp/inst", + "infraVlan": "vlan-4093", + "sysDesc": "topology/pod-1/node-203" + } + } + }, + { + "lldpInst": { + "attributes": { + "adminSt": "enabled", + "dn": "topology/pod-1/node-204/sys/lldp/inst", + "infraVlan": "vlan-4093", + "sysDesc": "topology/pod-1/node-204" + } + } + }, + { + "lldpInst": { + "attributes": { + "adminSt": "enabled", + "dn": "topology/pod-1/node-205/sys/lldp/inst", + "infraVlan": "vlan-4093", + "sysDesc": "topology/pod-1/node-205" + } + } + }, + { + "lldpInst": { + "attributes": { + "adminSt": "enabled", + "dn": "topology/pod-1/node-206/sys/lldp/inst", + "infraVlan": "vlan-4093", + "sysDesc": "topology/pod-1/node-206" + } + } + }, + { + "lldpInst": { + "attributes": { + "adminSt": "enabled", + "dn": "topology/pod-1/node-207/sys/lldp/inst", + "infraVlan": "vlan-4093", + "sysDesc": "topology/pod-1/node-207" + } + } + }, + { + "lldpInst": { + "attributes": { + "adminSt": "enabled", + "dn": "topology/pod-1/node-208/sys/lldp/inst", + "infraVlan": "vlan-4093", + "sysDesc": "topology/pod-1/node-208" + } + } + }, + { + "lldpInst": { + "attributes": { + "adminSt": "enabled", + "dn": "topology/pod-1/node-209/sys/lldp/inst", + "infraVlan": "vlan-4093", + "sysDesc": "topology/pod-1/node-209" + } + } + }, + { + "lldpInst": { + "attributes": { + "adminSt": "enabled", + "dn": "topology/pod-1/node-210/sys/lldp/inst", + "infraVlan": "vlan-4093", + "sysDesc": "topology/pod-1/node-210" + } + } + } +] diff --git a/tests/checks/infravlan_overlap_access_policy_check/lldpInst_infra_vlan_single_entry.json b/tests/checks/infravlan_overlap_access_policy_check/lldpInst_infra_vlan_single_entry.json new file mode 100644 index 0000000..5b14d8d --- /dev/null +++ b/tests/checks/infravlan_overlap_access_policy_check/lldpInst_infra_vlan_single_entry.json @@ -0,0 +1,12 @@ +[ + { + "lldpInst": { + "attributes": { + "adminSt": "enabled", + "dn": "topology/pod-1/node-201/sys/lldp/inst", + "infraVlan": "vlan-4093", + "sysDesc": "topology/pod-1/node-201" + } + } + } +] diff --git a/tests/checks/infravlan_overlap_access_policy_check/test_infravlan_overlap_access_policy_check.py b/tests/checks/infravlan_overlap_access_policy_check/test_infravlan_overlap_access_policy_check.py new file mode 100644 index 0000000..d7f0e09 --- /dev/null +++ b/tests/checks/infravlan_overlap_access_policy_check/test_infravlan_overlap_access_policy_check.py @@ -0,0 +1,151 @@ +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__)) + +test_function = "infravlan_overlap_access_policy_check" + +# icurl queries +lldpInst_api = "lldpInst.json?query-target-filter=wcard(lldpInst.dn,\"/node-1/\")" +fvnsEncapBlk_api = "fvnsEncapBlk.json" + + +@pytest.mark.parametrize( + "icurl_outputs, tversion, expected_result, expected_data", + [ + # Case 1: tversion missing. Expected: MANUAL. + ( + {}, + None, + script.MANUAL, + [], + ), + # Case 2: Version not affected (6.0(9f) is below 6.1(3f)). Expected: NA. + ( + {}, + "6.0(9f)", + script.NA, + [], + ), + # Case 3: Version not affected (6.1(3e) is just below lower boundary 6.1(3f)). Expected: NA. + ( + {}, + "6.1(3e)", + script.NA, + [], + ), + # Case 4: Version not affected (6.1(6a) is above upper boundary 6.1(5e) and not 6.2(1g)). Expected: NA. + ( + {}, + "6.1(6a)", + script.NA, + [], + ), + # Case 5: lldpInst returns no data so infraVlan cannot be determined. Expected: ERROR. + ( + { + lldpInst_api: read_data(dir, "lldpInst_empty.json"), + fvnsEncapBlk_api: read_data(dir, "fvnsEncapBlk_overlap_single_vlan_pool.json"), + }, + "6.2(1g)", + script.ERROR, + [], + ), + # Case 6: InfraVLAN overlaps on lower boundary version 6.1(3f). Expected: FAIL_UF. + ( + { + lldpInst_api: read_data(dir, "lldpInst_infra_vlan_multiple_entry.json"), + fvnsEncapBlk_api: read_data(dir, "fvnsEncapBlk_overlap_single_vlan_pool.json"), + }, + "6.1(3f)", + script.FAIL_UF, + [["4093", "vlan_pool", "vlan-100 to vlan-4094"]], + ), + # Case 7: InfraVLAN overlaps on a mid-range version 6.1(4a). Expected: FAIL_UF. + ( + { + lldpInst_api: read_data(dir, "lldpInst_infra_vlan_multiple_entry.json"), + fvnsEncapBlk_api: read_data(dir, "fvnsEncapBlk_overlap_single_vlan_pool.json"), + }, + "6.1(4a)", + script.FAIL_UF, + [["4093", "vlan_pool", "vlan-100 to vlan-4094"]], + ), + # Case 8: InfraVLAN overlaps on upper boundary version 6.1(5e). Expected: FAIL_UF. + ( + { + lldpInst_api: read_data(dir, "lldpInst_infra_vlan_multiple_entry.json"), + fvnsEncapBlk_api: read_data(dir, "fvnsEncapBlk_overlap_single_vlan_pool.json"), + }, + "6.1(5e)", + script.FAIL_UF, + [["4093", "vlan_pool", "vlan-100 to vlan-4094"]], + ), + # Case 9: InfraVLAN overlaps on standalone affected version 6.2(1g). Expected: FAIL_UF. + ( + { + lldpInst_api: read_data(dir, "lldpInst_infra_vlan_multiple_entry.json"), + fvnsEncapBlk_api: read_data(dir, "fvnsEncapBlk_overlap_single_vlan_pool.json"), + }, + "6.2(1g)", + script.FAIL_UF, + [["4093", "vlan_pool", "vlan-100 to vlan-4094"]], + ), + # Case 10: InfraVLAN does not overlap on affected version 6.2(1g). Expected: PASS. + ( + { + lldpInst_api: read_data(dir, "lldpInst_infra_vlan_multiple_entry.json"), + fvnsEncapBlk_api: read_data(dir, "fvnsEncapBlk_no_overlap.json"), + }, + "6.2(1g)", + script.PASS, + [], + ), + # Case 11: Single lldpInst entry with overlap on affected version 6.2(1g). Expected: FAIL_UF. + ( + { + lldpInst_api: read_data(dir, "lldpInst_infra_vlan_single_entry.json"), + fvnsEncapBlk_api: read_data(dir, "fvnsEncapBlk_overlap_single_vlan_pool.json"), + }, + "6.2(1g)", + script.FAIL_UF, + [["4093", "vlan_pool", "vlan-100 to vlan-4094"]], + ), + # Case 12: InfraVLAN overlaps on multiple vlan pools on standalone affected version 6.2(1g). Expected: FAIL_UF. + ( + { + lldpInst_api: read_data(dir, "lldpInst_infra_vlan_multiple_entry.json"), + fvnsEncapBlk_api: read_data(dir, "fvnsEncapBlk_overlap_multiple_vlan_pool.json"), + }, + "6.2(1g)", + script.FAIL_UF, + [ + ["4093", "vlan_pool1", "vlan-100 to vlan-4094"], + ["4093", "vlan_pool2", "vlan-4000 to vlan-4094"], + ], + ), + # Case 13: InfraVLAN exists and empty fvnsEncapBlk for vlan pools on standalone affected version 6.2(1g). Expected: PASS. + ( + { + lldpInst_api: read_data(dir, "lldpInst_infra_vlan_multiple_entry.json"), + fvnsEncapBlk_api: read_data(dir, "fvnsEncapBlk_empty.json"), + }, + "6.2(1g)", + script.PASS, + [], + ), + ], +) +def test_logic(run_check, mock_icurl, tversion, expected_result, expected_data): + result = run_check(tversion=script.AciVersion(tversion) if tversion else None) + assert result.result == expected_result + assert result.data == expected_data + + +