Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dcnm_vrf: Fix issue #352 #364

Open
wants to merge 60 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
40d93d6
Tentative fix for Issue #351
allenrobel Dec 3, 2024
0b17715
dcnm_vrf: to_bool() fix to return correct value, or call fail_json()
allenrobel Dec 3, 2024
6e32f42
dcnm_image_policy: fix for issue #347 (#348)
allenrobel Dec 3, 2024
244929b
dcnm_fabric: hardening (#349)
allenrobel Dec 3, 2024
5f67187
dcnm_vrf: remove bool() casts, more...
allenrobel Dec 4, 2024
9508840
dcnm_vrf: More refactoring and simplifying
allenrobel Dec 4, 2024
098d17f
Rename var for readability
allenrobel Dec 4, 2024
be41882
Rename var for readability
allenrobel Dec 4, 2024
13e076e
dcnm_vrf: Avoid code duplication
allenrobel Dec 5, 2024
6a6de04
Remove TODO comment
allenrobel Dec 5, 2024
12f9e53
dcnm_vrf: leverage get_vrf_lite_objects() everywhere
allenrobel Dec 5, 2024
bbbf285
Appease pylint f-string complaints, more...
allenrobel Dec 5, 2024
da54fa1
test_log_v2.py: Temporarily disable unit tests
allenrobel Dec 5, 2024
c8c578b
Appease pylint
allenrobel Dec 5, 2024
14da7d7
Fix pep8 too-many-blank-lines
allenrobel Dec 5, 2024
1e028a2
Remove python 3.9 incompatible type hint
allenrobel Dec 5, 2024
ee179b5
Re-enable test_log_v2.py unit tests and "fix" UT errors
allenrobel Dec 5, 2024
0c980b3
Appease linters
allenrobel Dec 5, 2024
5b8c1c3
Merge branch 'develop' into dcnm-vrf-fix-issue-351
allenrobel Dec 6, 2024
b401ab8
Update another conditional
allenrobel Dec 6, 2024
978b0d2
dcnm_vrf: dict_values_differ() use generic names
allenrobel Dec 6, 2024
75a24d5
dcnm_vrf: Address mwiebe review comments
allenrobel Dec 6, 2024
f747767
Address mwiebe coments part 2
allenrobel Dec 6, 2024
dbfe057
dcnm_vrf: Protect dictionary access
allenrobel Dec 6, 2024
cc804e0
dcnm_vrf: Refactor push_to_remote, validate_input
allenrobel Dec 7, 2024
78f9e0d
appease linters
allenrobel Dec 7, 2024
e861626
Appease linters
allenrobel Dec 7, 2024
3a1a514
More refactoring
allenrobel Dec 7, 2024
fed3a70
Fix typo
allenrobel Dec 7, 2024
930dbec
dcnm_vrf: fix IT files, minor cleanup
allenrobel Dec 8, 2024
d83d4fc
Fix UT failures
allenrobel Dec 8, 2024
832a3e8
Appease pylint
allenrobel Dec 8, 2024
cf27b33
Cleanup IT deleted, query, merged
allenrobel Dec 8, 2024
72bfc10
Update playbooks/roles
allenrobel Dec 8, 2024
5749eda
Move dynamic_inventory.py into playbooks/files
allenrobel Dec 8, 2024
acb3890
Appease ansible sanity
allenrobel Dec 8, 2024
0bc26be
Appease linters
allenrobel Dec 8, 2024
b78e996
dcnm_vrf: Updates to integration tests
allenrobel Dec 10, 2024
ca7bea8
Appease linters
allenrobel Dec 10, 2024
8fb0907
Fix unprotected dictionary access
allenrobel Dec 10, 2024
509c335
Update integration tests
allenrobel Dec 10, 2024
b61fd0b
dcnm_vrf/dcnm_tests.yaml - Include all vars
allenrobel Dec 11, 2024
a565a46
Update Usage section and assign additional fabric_* vars
allenrobel Dec 11, 2024
001931e
IT: interface var naming change
allenrobel Dec 11, 2024
ecd1849
IT: Update scale.yaml
allenrobel Dec 12, 2024
a0f44c3
dcnm_vrf: IT dynamic_inventory.py small modifications
allenrobel Dec 12, 2024
41a8fd7
dcnm_vrf: fix for #356, and for an undeploy case, simplify, more...
allenrobel Dec 15, 2024
c30cfd4
dcnm_vrf: Fix for issue #357
allenrobel Dec 16, 2024
d754e3e
dcnm_vrf IT: Update task titles, print results
allenrobel Dec 16, 2024
75334e0
Merge branch 'develop' into dcnm-vrf-fix-issue-351
allenrobel Dec 16, 2024
e92c1e2
IT: Update comment for workaround.
allenrobel Dec 17, 2024
a9d9c9e
Uncomment dcnm_get_ip_addr_info, more...
allenrobel Dec 18, 2024
2a19271
Safe get in dict_values_differ() method
mikewiebe Dec 18, 2024
0ecfe58
Change conf_changed to module scope
allenrobel Dec 18, 2024
a3ccb52
UT: Update unit tests to accommodate issue #357
allenrobel Dec 20, 2024
1ced9a1
dcnm_vrf: diff_for_create() consistent return statements
allenrobel Dec 20, 2024
baa756b
dcnm_vrf: fix for improper update of lanAttachList
allenrobel Dec 20, 2024
7e41d5d
Merge branch 'develop' into dcnm-vrf-fix-issue-351
allenrobel Dec 20, 2024
e1c4d1e
Initial commit
allenrobel Jan 10, 2025
4a4c398
Merge branch 'develop' into dcnm-vrf-fix-issue-352
allenrobel Jan 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 202 additions & 14 deletions plugins/modules/dcnm_vrf.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,7 @@
import logging
import re
import time
from typing import Tuple

from ansible.module_utils.basic import AnsibleModule
from ansible_collections.cisco.dcnm.plugins.module_utils.network.dcnm.dcnm import (
Expand All @@ -593,9 +594,35 @@
},
}

from ..module_utils.common.log_v2 import Log
from ..module_utils.network.dcnm.dcnm import (dcnm_get_ip_addr_info,
dcnm_get_url, dcnm_send,
dcnm_version_supported,
get_fabric_details,
get_fabric_inventory_details,
get_ip_sn_dict,
get_ip_sn_fabric_dict,
validate_list_of_dicts)

class DcnmVrf:
dcnm_vrf_paths = {
11: {
"GET_VRF": "/rest/top-down/fabrics/{}/vrfs",
"GET_VRF_ATTACH": "/rest/top-down/fabrics/{}/vrfs/attachments?vrf-names={}",
"GET_VRF_SWITCH": "/rest/top-down/fabrics/{}/vrfs/switches?vrf-names={}&serial-numbers={}",
"GET_VRF_ID": "/rest/managed-pool/fabrics/{}/partitions/ids",
"GET_VLAN": "/rest/resource-manager/vlan/{}?vlanUsageType=TOP_DOWN_VRF_VLAN",
},
12: {
"GET_VRF": "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{}/vrfs",
"GET_VRF_ATTACH": "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{}/vrfs/attachments?vrf-names={}",
"GET_VRF_SWITCH": "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{}/vrfs/switches?vrf-names={}&serial-numbers={}",
"GET_VRF_ID": "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/top-down/fabrics/{}/vrfinfo",
"GET_VLAN": "/appcenter/cisco/ndfc/api/v1/lan-fabric/rest/resource-manager/vlan/{}?vlanUsageType=TOP_DOWN_VRF_VLAN",
},
}


class DcnmVrf:
def __init__(self, module):
self.class_name = self.__class__.__name__

Expand Down Expand Up @@ -659,7 +686,12 @@ def __init__(self, module):
self.inventory_data = get_fabric_inventory_details(self.module, self.fabric)
self.ip_sn, self.hn_sn = get_ip_sn_dict(self.inventory_data)
self.sn_ip = {value: key for (key, value) in self.ip_sn.items()}
self.per_vrf_loopback_auto_provision = None
self.fabric_data = get_fabric_details(self.module, self.fabric)

msg = f"self.fabric_data: {json.dumps(self.fabric_data, indent=4, sort_keys=True)}"
self.log.debug(msg)

self.fabric_type = self.fabric_data.get("fabricType")
self.ip_fab, self.sn_fab = get_ip_sn_fabric_dict(self.inventory_data)
if self.dcnm_version > 12:
Expand Down Expand Up @@ -797,6 +829,50 @@ def compare_properties(dict1, dict2, property_list):
return False
return True

def set_fabric_properties_of_interest(self) -> None:
"""
# Summary

From self.fabric_data, extract parameters that are used within
the DcnmVrf class.

## Parameters set

self.per_vrf_loopback_auto_provision : bool

## Raises

None

## TODO: convert unit tests to pytest

Would be better to use module_utils/fabric/fabric_details_v2.py for this,
but anything related to RestSend() and Sender() causes unit tests based on
TestDcnmModule.execute_module() to fail.

Investigate converting all unit tests for DcnmVrf module to pytest.
"""
msg = f"self.fabric_data: {json.dumps(self.fabric_data, indent=4, sort_keys=True)}"
self.log.debug(msg)
nv_pairs = self.fabric_data.get("nvPairs")
if nv_pairs is None:
self.log.debug("Setting self.per_vrf_loopback_auto_provision = False")
self.per_vrf_loopback_auto_provision = False
return

value = nv_pairs.get("PER_VRF_LOOPBACK_AUTO_PROVISION")
if value == "false":
self.per_vrf_loopback_auto_provision = False
elif value == "true":
self.per_vrf_loopback_auto_provision = True
elif value is None:
self.per_vrf_loopback_auto_provision = False
else:
self.per_vrf_loopback_auto_provision = value
msg = f"self.per_vrf_loopback_auto_provision: type: {type(self.per_vrf_loopback_auto_provision)}, "
msg += f"value: {self.per_vrf_loopback_auto_provision}"
self.log.debug(msg)

def diff_for_attach_deploy(self, want_a, have_a, replace=False):
"""
# Summary
Expand Down Expand Up @@ -946,7 +1022,6 @@ def diff_for_attach_deploy(self, want_a, have_a, replace=False):
have_is_attached = self.to_bool("isAttached", have)

if have_is_attached != want_is_attached:

if "isAttached" in want:
del want["isAttached"]

Expand Down Expand Up @@ -2606,7 +2681,7 @@ def push_diff_detach(self, is_rollback=False):
if not self.diff_detach:
return

# For multiste fabric, update the fabric name to the child fabric
# For multisite fabric, update the fabric name to the child fabric
# containing the switches
if self.fabric_type == "MFD":
for elem in self.diff_detach:
Expand All @@ -2620,11 +2695,16 @@ def push_diff_detach(self, is_rollback=False):

action = "attach"
path = self.paths["GET_VRF"].format(self.fabric)
detach_path = path + "/attachments"

if self.per_vrf_loopback_auto_provision is True:
path += "/attachments?quick-Attach=true"
elif self.per_vrf_loopback_auto_provision is False:
path += "/attachments"
else:
path += "/attachments"

verb = "POST"
self.send_to_controller(
action, verb, detach_path, self.diff_detach, is_rollback
)
self.send_to_controller(action, verb, path, self.diff_detach, is_rollback)

def push_diff_undeploy(self, is_rollback=False):
"""
Expand Down Expand Up @@ -3162,7 +3242,13 @@ def push_diff_attach(self, is_rollback=False):
action = "attach"
verb = "POST"
path = self.paths["GET_VRF"].format(self.fabric)
attach_path = path + "/attachments"

if self.per_vrf_loopback_auto_provision is True:
path += "/attachments?quick-Attach=true"
elif self.per_vrf_loopback_auto_provision is False:
path += "/attachments"
else:
path += "/attachments"

# For multisite fabrics, update the fabric name to the child fabric
# containing the switches.
Expand All @@ -3171,9 +3257,7 @@ def push_diff_attach(self, is_rollback=False):
for node in elem["lanAttachList"]:
node["fabric"] = self.sn_fab[node["serialNumber"]]

self.send_to_controller(
action, verb, attach_path, new_diff_attach_list, is_rollback
)
self.send_to_controller(action, verb, path, new_diff_attach_list, is_rollback)

def push_diff_deploy(self, is_rollback=False):
"""
Expand Down Expand Up @@ -3528,6 +3612,61 @@ def vrf_spec(self):

return copy.deepcopy(spec)

def validate_vrf_lite_for_per_vrf_loopback_provision(
self, vrf_lite_list: list
) -> list[str]:
"""
# Summary

If the fabric is configured with PER_VRF_LOOPBACK_AUTO_PROVISION,
the playbook vrf_lite config must not contain certain keys, as
described in invalid_keys below.

This method verifies the above.

## Returns

- True if vrf_lite_dict is valid.
- False if vrf_lite_dict contains invalid key/values.
"""
msg = "ENTERED. "
msg += f"vrf_lite: {json.dumps(vrf_lite_list, indent=4, sort_keys=True)}"
self.log.debug(msg)

result: list[str] = []

if self.per_vrf_loopback_auto_provision is False:
msg = "per_vrf_loopback_auto_provision is not configured. "
msg += "Returning []"
self.log.debug(msg)
return result
invalid_keys = [
"ipv4_addr",
"neighbor_ipv4",
"ipv6_addr",
"neighbor_ipv6",
"dot1q",
]
msg = "per_vrf_loopback_auto_provision is configured on fabric, "
msg += f"{self.fabric}. "
msg += "The following vrf_lite configuration items must be null, "
msg += "but contain a value: "
bad_keys = ["key(value)"]
for vrf_lite_dict in vrf_lite_list:
for key in invalid_keys:
value = vrf_lite_dict.get(key)
if value is not None:
bad_keys.append(f"{key}({value})")
if len(bad_keys) != 0:
msg += ",".join(bad_keys)
self.log.debug(msg)
return [msg]
msg = "per_vrf_loopback_auto_provision is configured, "
msg += "and vrf_lite configuration is valid. "
msg = "Returning []"
self.log.debug(msg)
return []

def validate_input(self):
"""Parse the playbook values, validate to param specs."""
method_name = inspect.stack()[0][3]
Expand Down Expand Up @@ -3618,7 +3757,19 @@ def validate_input(self):
msg += f"{json.dumps(valid_lite, indent=4, sort_keys=True)}"
self.log.debug(msg)

lite["vrf_lite"] = valid_lite
msg = f"state {self.state}: "
msg += "invalid_lite: "
msg += f"{json.dumps(invalid_lite, indent=4, sort_keys=True)}"
self.log.debug(msg)

result = self.validate_vrf_lite_for_per_vrf_loopback_provision(
valid_lite
)

if result == []:
lite["vrf_lite"] = valid_lite
else:
invalid_lite.extend(result)
invalid_params.extend(invalid_lite)
self.validated.append(vrf)

Expand Down Expand Up @@ -3647,7 +3798,24 @@ def validate_input(self):
valid_lite, invalid_lite = validate_list_of_dicts(
lite["vrf_lite"], lite_spec
)
lite["vrf_lite"] = valid_lite
msg = f"state {self.state}: "
msg += "valid_lite: "
msg += f"{json.dumps(valid_lite, indent=4, sort_keys=True)}"
self.log.debug(msg)

msg = f"state {self.state}: "
msg += "invalid_lite: "
msg += f"{json.dumps(invalid_lite, indent=4, sort_keys=True)}"
self.log.debug(msg)

result = self.validate_vrf_lite_for_per_vrf_loopback_provision(
valid_lite
)

if result == []:
lite["vrf_lite"] = valid_lite
else:
invalid_lite.extend(result)
invalid_params.extend(invalid_lite)
self.validated.append(vrf)

Expand All @@ -3658,7 +3826,25 @@ def validate_input(self):
msg += f"{','.join(invalid_params)}"
self.module.fail_json(msg=msg)

def handle_response(self, res, op):
def handle_response(self, res, op) -> Tuple[bool, bool]:
"""
# Summary

Handle responses from the controller.

## Returns

- fail
- True if the response indicates failure.
- False otherwise.
- changed
- True if the response indicates something was changed.
- False otherwise

## Raises

None
"""
self.log.debug("ENTERED")

fail = False
Expand Down Expand Up @@ -3762,6 +3948,8 @@ def main():
msg += "does not have any switches"
module.fail_json(msg=msg)

dcnm_vrf.set_fabric_properties_of_interest()

dcnm_vrf.validate_input()

dcnm_vrf.get_want()
Expand Down
1 change: 1 addition & 0 deletions tests/unit/modules/dcnm/fixtures/dcnm_vrf.json
Original file line number Diff line number Diff line change
Expand Up @@ -1455,6 +1455,7 @@
"LOOPBACK100_IP_RANGE": "10.10.0.0/24",
"MS_LOOPBACK_ID": "100",
"MS_UNDERLAY_AUTOCONFIG": "true",
"PER_VRF_LOOPBACK_AUTO_PROVISION": "false",
"RP_SERVER_IP": "",
"TOR_AUTO_DEPLOY": "false",
"default_network": "Default_Network_Universal",
Expand Down
Loading