Skip to content

Commit

Permalink
99% unit test coverage for update.py
Browse files Browse the repository at this point in the history
Also:

- MockImagePolicies: modified to correctly handle ref_count and name when the policy does not exist in  all_policies

- delete.py: Added Summary to all unit tests

- update.py: make self.image_policies private (self._image_policies)

- update.py: fix typo in fail_json message

- update.py: fix docstring for _build_payloads_to_commit()

- update.py: in _build_payloads_to_commit() remove ref_count, imageName, and platformPolicies from the merged payload since these are not valid parameters for the edit-policy endpoint (these ARE returned by the controller, hence we have to remove them).

- update.py: _send_payloads(): directly set self.response_current, and self.result_current from the return values of their respective calls.

- update.py: _send_payloads(): out of paranoia, deepcopy the response, result, payload when assigning values to diff_*, response_* and result_*.

- update.py, ImagePolicyUpdate.payload setter: we needed to set payloads as well here. fixed.

- replace.py: move _default_policy() into ImagePolicyCommon()
  • Loading branch information
allenrobel committed Feb 16, 2024
1 parent 5c6d6bc commit fdfccde
Show file tree
Hide file tree
Showing 14 changed files with 2,018 additions and 94 deletions.
34 changes: 29 additions & 5 deletions plugins/module_utils/image_policy/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,14 @@ def _verify_image_policy_ref_count(self, instance, policy_names):
all devices before it/they can be deleted.
"""
method_name = inspect.stack()[0][3]
msg = f"GOT policy_names: {policy_names}. "
self.log.debug(msg)
_non_zero_ref_counts = {}
for policy_name in policy_names:
instance.policy_name = policy_name
msg = f"instance.policy_name: {instance.policy_name}, "
msg += f"instance.ref_count: {instance.ref_count}."
self.log.debug(msg)
# If the policy does not exist on the controller, the ref_count
# will be None. We skip these too.
msg = f"instance.ref_count: {instance.ref_count}. "
msg += f"instance.policy_name: {instance.policy_name}."
self.log.debug(msg)
if instance.ref_count in [0, None]:
continue
_non_zero_ref_counts[policy_name] = instance.ref_count
Expand All @@ -99,6 +97,32 @@ def _verify_image_policy_ref_count(self, instance, policy_names):
msg += f"ref_count: {ref_count}. "
self.ansible_module.fail_json(msg, **self.failed_result)

def _default_policy(self, policy_name):
"""
Return a default policy payload for policy name.
"""
method_name = inspect.stack()[0][3]
if not isinstance(policy_name, str):
msg = f"{self.class_name}.{method_name}: "
msg += "policy_name must be a string. "
msg += f"Got type {type(policy_name).__name__} for "
msg += f"value {policy_name}."
self.log.debug(msg)
self.ansible_module.fail_json(msg)

policy = {
"agnostic": False,
"epldImgName": "",
"nxosVersion": "",
"packageName": "",
"platform": "",
"policyDescr": "",
"policyName": policy_name,
"policyType": "PLATFORM",
"rpmimages": "",
}
return policy

def _handle_response(self, response, verb):
"""
Call the appropriate handler for response based on verb
Expand Down
26 changes: 0 additions & 26 deletions plugins/module_utils/image_policy/replace.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,32 +157,6 @@ def payloads(self, value):
self._verify_payload(item)
self.properties["payloads"] = value

def _default_policy(self, policy_name):
"""
Return a default policy payload for policy name.
"""
method_name = inspect.stack()[0][3]
if not isinstance(policy_name, str):
msg = f"{self.class_name}.{method_name}: "
msg += "policy_name must be a string. "
msg += f"Got type {type(policy_name).__name__} for "
msg += f"value {policy_name}."
self.log.debug(msg)
self.ansible_module.fail_json(msg)

policy = {
"agnostic": False,
"epldImgName": "",
"nxosVersion": "",
"packageName": "",
"platform": "",
"policyDescr": "",
"policyName": policy_name,
"policyType": "PLATFORM",
"rpmimages": "",
}
return policy

def _build_payloads_to_commit(self):
"""
Build the payloads to commit to the controller.
Expand Down
60 changes: 37 additions & 23 deletions plugins/module_utils/image_policy/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def __init__(self, ansible_module):
self.log.debug(msg)

self.endpoints = ApiEndpoints()
self.image_policies = ImagePolicies(self.ansible_module)
self._image_policies = ImagePolicies(self.ansible_module)

self.action = "update"
self._payloads_to_commit = []
Expand All @@ -77,7 +77,7 @@ def _verify_payload(self, payload):
if not isinstance(payload, dict):
msg = f"{self.class_name}.{method_name}: "
msg += "payload must be a dict. "
msg += f"gpt type {type(payload).__name__}, "
msg += f"Got type {type(payload).__name__}, "
msg += f"value {payload}"
self.ansible_module.fail_json(msg, **self.failed_result)

Expand All @@ -96,35 +96,45 @@ def _verify_payload(self, payload):
def _build_payloads_to_commit(self):
"""
Build a list of payloads to commit. Skip any payloads that
already exist on the controller.
do not exist on the controller.
Expects self.payloads to be a list of dict, with each dict
being a payload for the image policy edit API endpoint.
Populates self._payloads_to_commit with a list of payloads
to commit.
"""
self.image_policies.refresh()
self._image_policies.refresh()

_payloads = []
_policy_names = []
for payload in self.payloads:
if payload.get("policyName", None) not in self.image_policies.all_policies:
if payload.get("policyName", None) not in self._image_policies.all_policies:
continue
_payloads.append(payload)
_policy_names.append(payload["policyName"])

self._verify_image_policy_ref_count(self.image_policies, _policy_names)
self._verify_image_policy_ref_count(self._image_policies, _policy_names)

# build self._payloads_to_commit by merging _payloads with the policies
# on the controller. The parameters in _payloads take precedence.
# build self._payloads_to_commit by merging each _payload with the
# corresponding policy payload on the controller. The parameters
# in _payloads take precedence.
self._payloads_to_commit = []
for payload in _payloads:
merge = MergeDicts(self.ansible_module)
merge.dict1 = self.image_policies.all_policies.get(payload["policyName"])
merge.dict1 = self._image_policies.all_policies.get(payload["policyName"])
merge.dict2 = payload
merge.commit()
self._payloads_to_commit.append(copy.deepcopy(merge.dict_merged))
updated_payload = copy.deepcopy(merge.dict_merged)
# ref_count, imageName, and platformPolicies are returned
# by the controller, but are not valid parameters for the
# edit-policy endpoint.
updated_payload.pop("ref_count", None)
updated_payload.pop("imageName", None)
updated_payload.pop("platformPolicies", None)
self._payloads_to_commit.append(copy.deepcopy(updated_payload))
msg = f"self._payloads to commit: {json.dumps(self._payloads_to_commit, indent=4, sort_keys=True)}"
self.log.debug(msg)

def _send_payloads(self):
"""
Expand All @@ -144,19 +154,21 @@ def _send_payloads(self):
self.result_nok = []
self.diff_nok = []
for payload in self._payloads_to_commit:
response = dcnm_send(
self.response_current = dcnm_send(
self.ansible_module, self.verb, self.path, data=json.dumps(payload)
)
result = self._handle_response(response, self.verb)
self.result_current = self._handle_response(
self.response_current, self.verb
)

if result["success"]:
self.response_ok.append(response)
self.result_ok.append(result)
self.diff_ok.append(payload)
if self.result_current["success"]:
self.response_ok.append(copy.deepcopy(self.response_current))
self.result_ok.append(copy.deepcopy(self.result_current))
self.diff_ok.append(copy.deepcopy(payload))
else:
self.response_nok.append(response)
self.result_nok.append(result)
self.diff_nok.append(payload)
self.response_nok.append(copy.deepcopy(self.response_current))
self.result_nok.append(copy.deepcopy(self.result_current))
self.diff_nok.append(copy.deepcopy(payload))

def _process_responses(self):
method_name = inspect.stack()[0][3]
Expand Down Expand Up @@ -200,7 +212,7 @@ def _process_responses(self):
result["result"] = self.result

msg = f"{self.class_name}.{method_name}: "
msg += "Bad response(s) during policy update. "
msg += "Bad response(s) during image policy bulk update. "
msg += f"Bad responses: {self.response_nok}"
self.ansible_module.fail_json(msg, **result)

Expand Down Expand Up @@ -321,7 +333,7 @@ class ImagePolicyUpdate(ImagePolicyUpdateCommon):
Example (update one policy):
policy = {
image_policy_payload = {
"agnostic": false,
"epldImgName": "n9000-epld.10.3.2.F.img",
"nxosVersion": "10.3.1_nxos64-cs_64bit",
Expand All @@ -333,7 +345,7 @@ class ImagePolicyUpdate(ImagePolicyUpdateCommon):
"rpmimages": "mtx-grpctunnel-2.1.0.0-10.4.1.lib32_64_n9000"
}
update = ImagePolicyUpdate(ansible_module)
update.payload = policy
update.payload = image_policy_payload
update.commit()
"""

Expand All @@ -347,9 +359,10 @@ def __init__(self, ansible_module):
self._mandatory_keys = set()
self._mandatory_keys.add("policyName")

self._build_properties()
self.endpoints = ApiEndpoints()

self._build_properties()

def _build_properties(self):
"""
self.properties holds property values for the class
Expand All @@ -368,6 +381,7 @@ def payload(self):
@payload.setter
def payload(self, value):
self._verify_payload(value)
self.properties["payloads"] = [value]
self.properties["payload"] = value

def commit(self):
Expand Down
Loading

0 comments on commit fdfccde

Please sign in to comment.