From 4ee9bd6e11ee6ca71d8968f53004fa3026f5381e Mon Sep 17 00:00:00 2001 From: Shivam Sharma Date: Tue, 8 Oct 2024 09:19:39 -0400 Subject: [PATCH 01/10] create and modify feature --- .../omevv_firmware_repository_profile.py | 395 ++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 plugins/modules/omevv_firmware_repository_profile.py diff --git a/plugins/modules/omevv_firmware_repository_profile.py b/plugins/modules/omevv_firmware_repository_profile.py new file mode 100644 index 000000000..27daa2fc1 --- /dev/null +++ b/plugins/modules/omevv_firmware_repository_profile.py @@ -0,0 +1,395 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# +# Dell OpenManage Ansible Modules +# Version 9.8.0 +# Copyright (C) 2024 Dell Inc. or its subsidiaries. All Rights Reserved. + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +DOCUMENTATION = r""" +--- +module: omevv_firmware_repository_profile +short_description: Create, modify and delete firmware repository profile +version_added: "9.8.0" +description: + - This module allows to create, modify and delete firmware repository profile. +extends_documentation_fragment: + - dellemc.openmanage.omevv_auth_options +options: + state: + description: + - C(present) creates a new OMEVV firmware repository profile or modifies an existing profile if the profile with the same name already exists. + - C(absent) deletes the OMEVV firmware repository profile. + - Either I(profile_name) or I(profile_id) is required when I(state) is C(absent). + type: str + choices: [present, absent] + default: present + name: + description: + - Name of the profile. + - This is required for modification operation and when I(state) is C(absent). + type: str + description: + description: + - Description of OMEVV firmware repository profile.. + type: str + new_name: + description: New profile name when modify operation is performed. + type: str + protocol_type: + description: + - C(NFS) represents NFS share path. + - C(CIFS) represents NFS share path. + - C(HTTP) represents HTTP share path. + - C(HTTPS) represents HTTPS share path. + - This is required when I(state) is C(present) and when creating a new profile. + type: str + choices: [NFS, CIFS, HTTP, HTTPS] + catalog_path: + description: + - Absolute path of the catalog. + - HTTP, HTTPS, NFS, and CIFS paths are supported. + - This parameter is required when I(state) is C(present). + type: str + share_username: + description: + - Username of the share. + - This is required when I(catalog_path) is HTTPS or CIFS. + type: str + share_password: + description: + - Password of the share. + - This is required when I(catalog_path) is HTTPS or CIFS. + type: str + share_domain: + description: Domain of the share. + type: str +requirements: + - "python >= 3.9.6" +author: + - "Shivam Sharma(@ShivamSh3)" +attributes: + check_mode: + description: Runs task to validate without performing action on the target machine. + support: full + diff_mode: + description: Runs the task to report the changes made or to be made. + support: full +notes: + - Run this module from a system that has direct access to Dell OpenManage Enterprise. + - This module supports IPv4 and IPv6 addresses. +""" + +EXAMPLES = r""" +--- +- name: Create a firmware repository profile + dellemc.openmanage.omevv_firmware_repository_profile: + hostname: "192.168.0.1" + vcenter_uuid: "xxxxx" + vcenter_username: "username" + vcenter_password: "password" + ca_path: "path/to/ca_file" + state: present + name: profile-1 + catalog_path: http://xx.xx.xx.xx/share/Catalog/Catalog.xml + +- name: Modify a firmware repository profile + dellemc.openmanage.omevv_firmware_repository_profile: + hostname: "192.168.0.1" + vcenter_uuid: "xxxxx" + vcenter_username: "username" + vcenter_password: "password" + ca_path: "path/to/ca_file" + state: present + name: profile-1 + new_name: profile-2 + catalog_path: http://xx.xx.xx.xx/new_share/Catalog/Catalog.xml + +- name: Delete a firmware repository profile + dellemc.openmanage.omevv_firmware_repository_profile: + hostname: "192.168.0.1" + vcenter_uuid: "xxxxx" + vcenter_username: "username" + vcenter_password: "password" + ca_path: "path/to/ca_file" + state: absent + name: profile-1 +""" + +RETURN = r''' +--- +msg: + type: str + description: Status of the profile operation. + returned: always + sample: "Successfully created the OMEVV firmware repository profile." +error_info: + description: Details of the HTTP Error. + returned: on HTTP error + type: dict + sample: + { + "errorCode": "18001", + "message": "Repository profile with name Test already exists." + } +''' +import json +from ssl import SSLError +from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError +from ansible.module_utils.urls import ConnectionError +from ansible_collections.dellemc.openmanage.plugins.module_utils.omevv import RestOMEVV, OMEVVAnsibleModule +from ansible_collections.dellemc.openmanage.plugins.module_utils.utils import remove_key + +ODATA_REGEX = "(.*?)@odata" +ODATA = "@odata.id" +XML_EXT = ".xml" +GZ_EXT = ".gz" +XML_GZ_EXT = ".xml.gz" +MESSAGE_EXTENDED_INFO = "@Message.ExtendedInfo" +TEST_CONNECTION_URI = "/RepositoryProfiles/TestConnection" +PROFILE_URI = "/RepositoryProfiles" +SUCCESS_CREATION_MSG = "Successfully created the OMEVV firmware repository profile." +SAME_PROFILE_MSG = "Firmware repository profile with name {profile_name} already exists." +FAILED_CREATION_MSG = "Unable to create the OMEVV firmware repository profile." +SUCCESS_MODIFY_MSG = "Successfully modified the OMEVV firmware repository profile." +FAILED_MODIFY_MSG = "Unable to modify the OMEVV firmware repository profile as the details are same." +FAILED_CONN_MSG = "Unable to complete the operation. Please check the connection details." +CHANGES_FOUND_MSG = "Changes found to be applied." +CHANGES_NOT_FOUND_MSG = "No changes found to be applied." + + +class FirmwareRepositoryProfile: + + def __init__(self, module, rest_obj): + self.module = module + self.obj = rest_obj + + def get_payload_details(self): + payload = {} + payload["profileName"] = self.module.params.get('name') + payload["protocolType"] = self.module.params.get('protocol_type').upper() + payload["sharePath"] = self.module.params.get('catalog_path') + payload["description"] = self.module.params.get('description') + payload["profileType"] = "Firmware" + payload["shareCredential"] = { + "username": self.module.params.get('share_username'), + "password": self.module.params.get('share_password'), + "domain": self.module.params.get('share_dommain') + } + return payload + + def form_conn_payload(self): + payload = self.get_payload_details() + del payload["profileName"] + del payload["sharePath"] + payload["catalogPath"] = self.module.params.get('catalog_path') + del payload["description"] + del payload["profileType"] + payload["checkCertificate"] = False + return payload + + def get_modify_payload_details(self): + payload = {} + payload["profileName"] = self.module.params.get('new_name') + payload["sharePath"] = self.module.params.get('catalog_path') + payload["description"] = self.module.params.get('description') + payload["shareCredential"] = { + "username": self.module.params.get('share_username'), + "password": self.module.params.get('share_password'), + "domain": self.module.params.get('share_dommain') + } + return payload + + def test_connection(self): + payload = self.form_conn_payload() + resp = self.obj.invoke_request("POST", TEST_CONNECTION_URI, payload) + if resp.success: + return True + else: + self.module.exit_json(msg=FAILED_CONN_MSG, failed=True) + + def get_firmware_repository_profile(self): + res = FirmwareRepositoryProfile.execute(self) + if res: + resp = self.obj.invoke_request("GET", PROFILE_URI) + data = resp.json_data + return data + + def search_profile_name(self, data, profile_name): + for d in data: + if d.get('profileName') == profile_name: + return d + return {} + + def validate_catalog_path(self, protocol_type, catalog_path): + protocol_mapping = { + 'CIFS': (lambda path: path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT)), + 'NFS': (lambda path: path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT)), + 'HTTP': (lambda path: path.startswith('http://') and (path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT))), + "HTTPS": (lambda path: path.startswith('https://') and (path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT))) + } + validator = protocol_mapping.get(protocol_type) + if validator is None: + self.module.exit_json(msg="Invalid catalog_path", failed=True) + if not validator(catalog_path): + self.module.exit_json(msg="Invalid catalog_path", failed=True) + + def execute(self): + self.validate_catalog_path(self.module.params.get('protocol_type'), self.module.params.get('catalog_path')) + result = self.test_connection() + return result + + +class CreateFirmwareRepositoryProfile(FirmwareRepositoryProfile): + + def __init__(self, module, rest_obj): + self.module = module + self.obj = rest_obj + + def create_firmware_repository_profile(self): + diff = {} + payload = self.get_payload_details() + res = FirmwareRepositoryProfile.execute(self) + if res: + if self.module._diff: + diff = dict( + before={}, + after=payload + ) + self.module.exit_json(changed=True, diff=diff) + resp = self.obj.invoke_request("POST", PROFILE_URI, payload) + if resp.success: + self.module.exit_json(msg=SUCCESS_CREATION_MSG, changed=True) + else: + self.module.exit_json(msg=FAILED_CREATION_MSG, failed=True) + + def execute(self): + result = self.get_firmware_repository_profile() + profile = self.module.params.get('name') + new_profile = self.module.params.get('new_name') + profile_exists = self.search_profile_name(result, profile) + if not profile_exists and self.module.check_mode: + self.module.exit_json(msg=CHANGES_FOUND_MSG, changed=True) + if not profile_exists and not self.module.check_mode: + result = self.create_firmware_repository_profile() + return result + if profile_exists and self.module.check_mode and not new_profile: + self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False) + if profile_exists and new_profile: + omevv_obj = ModifyFirmwareRepositoryProfile(self.module, self.obj) + omevv_obj.execute() + else: + self.module.exit_json(msg=SAME_PROFILE_MSG.format(profile_name=profile), skipped=True) + + +class ModifyFirmwareRepositoryProfile(FirmwareRepositoryProfile): + + def __init__(self, module, rest_obj): + self.module = module + self.obj = rest_obj + + def diff_check(self, api_response, module_response): + diff = {} + del module_response["protocolType"] + del module_response["shareCredential"] + for key in module_response.keys(): + if key not in api_response or api_response[key] != module_response[key]: + diff[key] = module_response[key] + return diff + + def trim_api_response(self, api_response): + trimmed_resp = {} + trimmed_resp["profileName"] = api_response["profileName"] + trimmed_resp["sharePath"] = api_response["sharePath"] + trimmed_resp["description"] = api_response["description"] + return trimmed_resp + + def modify_firmware_repository_profile(self, api_response): + diff = {} + MODIFY_PROFILE_URI = PROFILE_URI + "/" + str(api_response["id"]) + payload = self.get_modify_payload_details() + res = FirmwareRepositoryProfile.execute(self) + if res: + if self.module._diff: + del payload["shareCredential"] + diff = dict( + before=self.trim_api_response(api_response), + after=payload + ) + self.module.exit_json(changed=True, diff=diff) + resp = self.obj.invoke_request("PUT", MODIFY_PROFILE_URI, payload) + if resp.success: + self.module.exit_json(msg=SUCCESS_MODIFY_MSG, changed=True) + else: + self.module.exit_json(msg=FAILED_MODIFY_MSG, failed=True) + + def execute(self): + result = self.get_firmware_repository_profile() + profile = self.module.params.get('name') + api_response = self.search_profile_name(result, profile) + module_response = self.get_payload_details() + diff = self.diff_check(api_response, module_response) + if diff and self.module.check_mode: + self.module.exit_json(msg=CHANGES_FOUND_MSG, changed=True) + if diff and not self.module.check_mode: + result = self.modify_firmware_repository_profile(api_response) + return result + if not diff and self.module.check_mode: + self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False) + else: + self.module.exit_json(msg=SAME_PROFILE_MSG.format(profile_name=profile), skipped=True) + + +def main(): + argument_spec = { + "state": {"type": 'str', "choices": ['present', 'absent'], "default": 'present'}, + "share_username": {"type": 'str'}, + "share_password": {"type": 'str', "no_log": True}, + "name": {"type": 'str'}, + "new_name": {"type": 'str'}, + "catalog_path": {"type": 'str'}, + "description": {"type": 'str'}, + "protocol_type": {"type": 'str', "choices": ['NFS', 'CIFS', 'HTTP', 'HTTPS']}, + "share_domain": {"type": 'str'} + } + module = OMEVVAnsibleModule( + argument_spec=argument_spec, + required_if=[ + ["state", 'present', ("name", "catalog_path", "description", "protocol_type")], + ["protocol_type", "NFS", ("catalog_path", "share_domain")], + ["protocol_type", "CIFS", ("catalog_path", "share_username", "share_password", "share_domain")], + ["protocol_type", "HTTP", ("catalog_path", "share_domain")], + ["protocol_type", "HTTPS", ("catalog_path", "share_username", "share_password", "share_domain")], + ["state", 'absent', ("name",)] + ], + supports_check_mode=True) + try: + with RestOMEVV(module.params) as rest_obj: + omevv_obj = CreateFirmwareRepositoryProfile(module, rest_obj) + omevv_obj.execute() + except HTTPError as err: + filter_err = remove_key(json.load(err), regex_pattern=ODATA_REGEX) + code = filter_err.get('errorCode') + message = filter_err.get('message') + if '18001' in code: + module.exit_json(msg=message, skipped=True) + if '500' in code: + module.exit_json(msg=message, skipped=True) + module.fail_json(msg=str(err), error_info=json.load(err)) + except URLError as err: + module.exit_json(msg=str(err), unreachable=True) + except (IOError, ValueError, SSLError, TypeError, ConnectionError, + AttributeError, IndexError, KeyError, OSError) as err: + module.fail_json(msg=str(err)) + + +if __name__ == '__main__': + main() From 06521127159878f7aa1ed3ff1d9f2914f58f3a0f Mon Sep 17 00:00:00 2001 From: Shivam Sharma Date: Wed, 9 Oct 2024 05:35:10 -0400 Subject: [PATCH 02/10] delete feature --- .../omevv_firmware_repository_profile.py | 69 +++++++++++++++---- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/plugins/modules/omevv_firmware_repository_profile.py b/plugins/modules/omevv_firmware_repository_profile.py index 27daa2fc1..c63418f44 100644 --- a/plugins/modules/omevv_firmware_repository_profile.py +++ b/plugins/modules/omevv_firmware_repository_profile.py @@ -85,7 +85,6 @@ support: full notes: - Run this module from a system that has direct access to Dell OpenManage Enterprise. - - This module supports IPv4 and IPv6 addresses. """ EXAMPLES = r""" @@ -142,17 +141,17 @@ } ''' import json -from ssl import SSLError from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError from ansible.module_utils.urls import ConnectionError from ansible_collections.dellemc.openmanage.plugins.module_utils.omevv import RestOMEVV, OMEVVAnsibleModule -from ansible_collections.dellemc.openmanage.plugins.module_utils.utils import remove_key ODATA_REGEX = "(.*?)@odata" ODATA = "@odata.id" XML_EXT = ".xml" GZ_EXT = ".gz" XML_GZ_EXT = ".xml.gz" +HTTP_PATH = "http://" +HTTPS_PATH = "https://" MESSAGE_EXTENDED_INFO = "@Message.ExtendedInfo" TEST_CONNECTION_URI = "/RepositoryProfiles/TestConnection" PROFILE_URI = "/RepositoryProfiles" @@ -161,6 +160,9 @@ FAILED_CREATION_MSG = "Unable to create the OMEVV firmware repository profile." SUCCESS_MODIFY_MSG = "Successfully modified the OMEVV firmware repository profile." FAILED_MODIFY_MSG = "Unable to modify the OMEVV firmware repository profile as the details are same." +SUCCESS_DELETION_MSG = "Successfully deleted the OMEVV firmware repository profile." +FAILED_DELETION_MSG = "Unable to delete the OMEVV firmware repository profile." +PROFILE_NOT_FOUND_MSG = "Unable to delete the profile {profile_name} because the profile name is invalid. Enter a valid profile name and retry the operation." FAILED_CONN_MSG = "Unable to complete the operation. Please check the connection details." CHANGES_FOUND_MSG = "Changes found to be applied." CHANGES_NOT_FOUND_MSG = "No changes found to be applied." @@ -233,8 +235,8 @@ def validate_catalog_path(self, protocol_type, catalog_path): protocol_mapping = { 'CIFS': (lambda path: path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT)), 'NFS': (lambda path: path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT)), - 'HTTP': (lambda path: path.startswith('http://') and (path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT))), - "HTTPS": (lambda path: path.startswith('https://') and (path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT))) + 'HTTP': (lambda path: path.startswith(HTTP_PATH) and (path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT))), + "HTTPS": (lambda path: path.startswith(HTTPS_PATH) and (path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT))) } validator = protocol_mapping.get(protocol_type) if validator is None: @@ -348,6 +350,46 @@ def execute(self): self.module.exit_json(msg=SAME_PROFILE_MSG.format(profile_name=profile), skipped=True) +class DeleteFirmwareRepositoryProfile(FirmwareRepositoryProfile): + + def __init__(self, module, rest_obj): + self.module = module + self.obj = rest_obj + + def delete_firmware_repository_profile(self, api_response): + diff = {} + payload = self.get_payload_details() + DELETE_PROFILE_URI = PROFILE_URI + "/" + str(api_response["id"]) + res = FirmwareRepositoryProfile.execute(self) + if res: + if self.module._diff: + diff = dict( + before=payload, + after={} + ) + self.module.exit_json(changed=True, diff=diff) + resp = self.obj.invoke_request("DELETE", DELETE_PROFILE_URI) + if resp.success: + self.module.exit_json(msg=SUCCESS_DELETION_MSG, changed=True) + else: + self.module.exit_json(msg=FAILED_DELETION_MSG, failed=True) + + def execute(self): + result = self.get_firmware_repository_profile() + profile = self.module.params.get('name') + api_response = self.search_profile_name(result, profile) + profile_exists = self.search_profile_name(result, profile) + if not profile_exists and self.module.check_mode: + self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False) + if not profile_exists and not self.module.check_mode: + self.module.exit_json(msg=PROFILE_NOT_FOUND_MSG.format(profile_name=profile), changed=False) + if profile_exists and not self.module.check_mode: + result = self.delete_firmware_repository_profile(api_response) + return result + if profile_exists and self.module.check_mode: + self.module.exit_json(msg=CHANGES_FOUND_MSG, changed=True) + + def main(): argument_spec = { "state": {"type": 'str', "choices": ['present', 'absent'], "default": 'present'}, @@ -373,22 +415,25 @@ def main(): supports_check_mode=True) try: with RestOMEVV(module.params) as rest_obj: - omevv_obj = CreateFirmwareRepositoryProfile(module, rest_obj) + if module.params.get('state') == 'present': + omevv_obj = CreateFirmwareRepositoryProfile(module, rest_obj) + if module.params.get('state') == 'absent': + omevv_obj = DeleteFirmwareRepositoryProfile(module, rest_obj) omevv_obj.execute() except HTTPError as err: - filter_err = remove_key(json.load(err), regex_pattern=ODATA_REGEX) - code = filter_err.get('errorCode') - message = filter_err.get('message') + error_info = json.load(err) + code = error_info.get('errorCode') + message = error_info.get('message') if '18001' in code: module.exit_json(msg=message, skipped=True) if '500' in code: module.exit_json(msg=message, skipped=True) - module.fail_json(msg=str(err), error_info=json.load(err)) + module.exit_json(msg=message, error_info=error_info, failed=True) except URLError as err: module.exit_json(msg=str(err), unreachable=True) - except (IOError, ValueError, SSLError, TypeError, ConnectionError, + except (IOError, ValueError, TypeError, ConnectionError, AttributeError, IndexError, KeyError, OSError) as err: - module.fail_json(msg=str(err)) + module.exit_json(msg=str(err), failed=True) if __name__ == '__main__': From 23ced8369cdc651037748f4c3b240534880f4feb Mon Sep 17 00:00:00 2001 From: Shivam Sharma Date: Wed, 16 Oct 2024 01:26:50 -0400 Subject: [PATCH 03/10] defects fixes --- .../omevv_firmware_repository_profile.rst | 213 +++++++++++++++ plugins/module_utils/omevv_firmware_utils.py | 88 +++++++ .../omevv_firmware_repository_profile.py | 183 ++++++++----- .../test_omevv_firmware_repository_profile.py | 242 ++++++++++++++++++ 4 files changed, 655 insertions(+), 71 deletions(-) create mode 100644 docs/modules/omevv_firmware_repository_profile.rst create mode 100644 plugins/module_utils/omevv_firmware_utils.py create mode 100644 tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py diff --git a/docs/modules/omevv_firmware_repository_profile.rst b/docs/modules/omevv_firmware_repository_profile.rst new file mode 100644 index 000000000..70f5cfd60 --- /dev/null +++ b/docs/modules/omevv_firmware_repository_profile.rst @@ -0,0 +1,213 @@ +.. _omevv_firmware_repository_profile_module: + + +omevv_firmware_repository_profile -- Create, modify, and delete OMEVV firmware repository profile +================================================================================================= + +.. contents:: + :local: + :depth: 1 + + +Synopsis +-------- + +This module allows you to create, modify, or delete an OpenManage Enterprise Integration for VMware Center (OMEVV) firmware repository profile. + + + +Requirements +------------ +The below requirements are needed on the host that executes this module. + +- python \>= 3.9.6 + + + +Parameters +---------- + + state (optional, str, present) + \ :literal:`present`\ creates an OMEVV firmware repository profile or modifies an existing profile if the profile with the same name exists. + + \ :literal:`absent`\ deletes the OMEVV firmware repository profile. + + Either \ :emphasis:`profile\_name`\ or \ :emphasis:`profile\_id`\ is required when \ :emphasis:`state`\ is \ :literal:`absent`\ . + + + name (optional, str, None) + Name of the OMEVV firmware repository profile. + + This parameter is required for modification operation when \ :emphasis:`state`\ is \ :literal:`absent`\ . + + + description (optional, str, None) + Description of OMEVV firmware repository profile. + + + new_name (optional, str, None) + Name of the new OMEVV profile name when modify operation is performed. + + + protocol_type (optional, str, None) + \ :literal:`NFS`\ represents the NFS share path. + + \ :literal:`CIFS`\ represents the NFS share path. + + \ :literal:`HTTP`\ represents the HTTP share path. + + \ :literal:`HTTPS`\ represents the HTTPS share path. + + This parameter is required when \ :emphasis:`state`\ is \ :literal:`present`\ and a new profile is created. + + + catalog_path (optional, str, None) + Absolute path of the catalog. + + HTTP, HTTPS, NFS, and CIFS paths are supported. + + This parameter is required when \ :emphasis:`state`\ is \ :literal:`present`\ . + + + share_username (optional, str, None) + Username of the share. + + This parameter is required when \ :emphasis:`catalog\_path`\ is HTTPS or CIFS. + + + share_password (optional, str, None) + Password of the share. + + This parameter is required when \ :emphasis:`catalog\_path`\ is HTTPS or CIFS. + + + share_domain (optional, str, None) + Domain of the share. + + + hostname (True, str, None) + OpenManage Enterprise or OpenManage Enterprise Modular IP address or hostname. + + + vcenter_username (False, str, None) + OpenManage Enterprise Integration for VMware vCenter username. + + If the username is not provided, then the environment variable \ :envvar:`OMEVV\_VCENTER\_USERNAME`\ is used. + + Example: export OMEVV\_VCENTER\_USERNAME=username + + + vcenter_password (False, str, None) + OpenManage Enterprise Integration for VMware vCenter password. + + If the password is not provided, then the environment variable \ :envvar:`OMEVV\_VCENTER\_PASSWORD`\ is used. + + Example: export OMEVV\_VCENTER\_PASSWORD=password + + + vcenter_uuid (False, str, None) + Universally unique identifier (uuid) of vCenter. + + vCenter uuid details can be fetched using \ :ref:`dellemc.openmanage.omevv\_vcenter\_info `\ module. + + If the uuid is not provided, then the environment variable \ :envvar:`OMEVV\_VCENTER\_UUID`\ is used. + + Example: export OMEVV\_VCENTER\_UUID=uuid + + + port (optional, int, 443) + OpenManage Enterprise HTTPS port. + + + validate_certs (optional, bool, True) + Whether to check SSL certificate. - If \ :literal:`true`\ , the SSL certificates will be validated. - If \ :literal:`false`\ , the SSL certificates will not be validated. + + + ca_path (optional, path, None) + The Privacy Enhanced Mail (PEM) file that contains a CA certificate to be used for the validation. + + + timeout (optional, int, 30) + The socket level timeout in seconds. + + + + + +Notes +----- + +.. note:: + - Run this module from a system that has direct access to Dell OpenManage Enterprise. + + + + +Examples +-------- + +.. code-block:: yaml+jinja + + + --- + - name: Create an OMEVV firmware repository profile + dellemc.openmanage.omevv_firmware_repository_profile: + hostname: "192.168.0.1" + vcenter_uuid: "xxxxx" + vcenter_username: "username" + vcenter_password: "password" + ca_path: "path/to/ca_file" + state: present + name: profile-1 + catalog_path: http://xx.xx.xx.xx/share/Catalog/Catalog.xml + + - name: Modify an OMEVV firmware repository profile + dellemc.openmanage.omevv_firmware_repository_profile: + hostname: "192.168.0.1" + vcenter_uuid: "xxxxx" + vcenter_username: "username" + vcenter_password: "password" + ca_path: "path/to/ca_file" + state: present + name: profile-1 + new_name: profile-2 + catalog_path: http://xx.xx.xx.xx/new_share/Catalog/Catalog.xml + + - name: Delete an OMEVV firmware repository profile + dellemc.openmanage.omevv_firmware_repository_profile: + hostname: "192.168.0.1" + vcenter_uuid: "xxxxx" + vcenter_username: "username" + vcenter_password: "password" + ca_path: "path/to/ca_file" + state: absent + name: profile-1 + + + +Return Values +------------- + +msg (always, str, Successfully created the OMEVV firmware repository profile.) + Status of the profile operation. + + +error_info (on HTTP error, dict, {'errorCode': '18001', 'message': 'Repository profile with name Test already exists.'}) + Details of the HTTP Error. + + + + + +Status +------ + + + + + +Authors +~~~~~~~ + +- Shivam Sharma(@ShivamSh3) + diff --git a/plugins/module_utils/omevv_firmware_utils.py b/plugins/module_utils/omevv_firmware_utils.py new file mode 100644 index 000000000..4a75ba9df --- /dev/null +++ b/plugins/module_utils/omevv_firmware_utils.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- + +# Dell OpenManage Ansible Modules +# Version 9.8.0 +# Copyright (C) 2024 Dell Inc. or its subsidiaries. All Rights Reserved. + +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: + +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. + +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +PROFILE_URI = "/RepositoryProfiles" +TEST_CONNECTION_URI = "/RepositoryProfiles/TestConnection" + + +class OMEVVFirmwareProfile: + def __init__(self, omevv, module): + self.omevv = omevv + self.module = module + + def test_connection(self, payload): + """ + Tests the connection to the vCenter server. + + """ + resp = self.omevv.invoke_request("POST", TEST_CONNECTION_URI, payload) + return resp + + def get_firmware_repository_profile(self): + """ + Retrieves all firmware repository profile Information. + + """ + resp = self.omevv.invoke_request("GET", PROFILE_URI) + return resp + + def get_specific_firmware_repository_profile(self, profile_id): + """ + Retrieves all firmware repository profile Information. + + """ + resp = self.omevv.invoke_request("GET", PROFILE_URI + "/" + str(profile_id)) + return resp + + def create_firmware_repository_profile(self, payload): + """ + Creates a firmware repository profile. + + """ + resp = self.omevv.invoke_request("POST", PROFILE_URI, payload) + return resp + + def modify_firmware_repository_profile(self, profile_id, payload): + """ + Modifies a firmware repository profile. + + """ + resp = self.omevv.invoke_request("PUT", PROFILE_URI + "/" + str(profile_id), payload) + return resp + + def delete_firmware_repository_profile(self, profile_id): + """ + Deletes a firmware repository profile. + + """ + resp = self.omevv.invoke_request("DELETE", PROFILE_URI + "/" + str(profile_id)) + return resp diff --git a/plugins/modules/omevv_firmware_repository_profile.py b/plugins/modules/omevv_firmware_repository_profile.py index c63418f44..d6cf44a10 100644 --- a/plugins/modules/omevv_firmware_repository_profile.py +++ b/plugins/modules/omevv_firmware_repository_profile.py @@ -17,16 +17,15 @@ DOCUMENTATION = r""" --- module: omevv_firmware_repository_profile -short_description: Create, modify and delete firmware repository profile +short_description: Create, modify, and delete OMEVV firmware repository profile version_added: "9.8.0" -description: - - This module allows to create, modify and delete firmware repository profile. +description: This module allows you to create, modify, or delete an OpenManage Enterprise Integration for VMware Center (OMEVV) firmware repository profile. extends_documentation_fragment: - dellemc.openmanage.omevv_auth_options options: state: description: - - C(present) creates a new OMEVV firmware repository profile or modifies an existing profile if the profile with the same name already exists. + - C(present) creates an OMEVV firmware repository profile or modifies an existing profile if the profile with the same name exists. - C(absent) deletes the OMEVV firmware repository profile. - Either I(profile_name) or I(profile_id) is required when I(state) is C(absent). type: str @@ -34,23 +33,23 @@ default: present name: description: - - Name of the profile. - - This is required for modification operation and when I(state) is C(absent). + - Name of the OMEVV firmware repository profile. + - This parameter is required for modification operation when I(state) is C(absent). type: str description: description: - - Description of OMEVV firmware repository profile.. + - Description of OMEVV firmware repository profile. type: str new_name: - description: New profile name when modify operation is performed. + description: Name of the new OMEVV profile name when modify operation is performed. type: str protocol_type: description: - - C(NFS) represents NFS share path. - - C(CIFS) represents NFS share path. - - C(HTTP) represents HTTP share path. - - C(HTTPS) represents HTTPS share path. - - This is required when I(state) is C(present) and when creating a new profile. + - C(NFS) represents the NFS share path. + - C(CIFS) represents the NFS share path. + - C(HTTP) represents the HTTP share path. + - C(HTTPS) represents the HTTPS share path. + - This parameter is required when I(state) is C(present) and a new profile is created. type: str choices: [NFS, CIFS, HTTP, HTTPS] catalog_path: @@ -62,12 +61,12 @@ share_username: description: - Username of the share. - - This is required when I(catalog_path) is HTTPS or CIFS. + - This parameter is required when I(catalog_path) is HTTPS or CIFS. type: str share_password: description: - Password of the share. - - This is required when I(catalog_path) is HTTPS or CIFS. + - This parameter is required when I(catalog_path) is HTTPS or CIFS. type: str share_domain: description: Domain of the share. @@ -81,7 +80,7 @@ description: Runs task to validate without performing action on the target machine. support: full diff_mode: - description: Runs the task to report the changes made or to be made. + description: Runs the task to report the changes that are made or the changes that must be applied. support: full notes: - Run this module from a system that has direct access to Dell OpenManage Enterprise. @@ -89,7 +88,7 @@ EXAMPLES = r""" --- -- name: Create a firmware repository profile +- name: Create an OMEVV firmware repository profile dellemc.openmanage.omevv_firmware_repository_profile: hostname: "192.168.0.1" vcenter_uuid: "xxxxx" @@ -100,7 +99,7 @@ name: profile-1 catalog_path: http://xx.xx.xx.xx/share/Catalog/Catalog.xml -- name: Modify a firmware repository profile +- name: Modify an OMEVV firmware repository profile dellemc.openmanage.omevv_firmware_repository_profile: hostname: "192.168.0.1" vcenter_uuid: "xxxxx" @@ -112,7 +111,7 @@ new_name: profile-2 catalog_path: http://xx.xx.xx.xx/new_share/Catalog/Catalog.xml -- name: Delete a firmware repository profile +- name: Delete an OMEVV firmware repository profile dellemc.openmanage.omevv_firmware_repository_profile: hostname: "192.168.0.1" vcenter_uuid: "xxxxx" @@ -144,19 +143,19 @@ from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError from ansible.module_utils.urls import ConnectionError from ansible_collections.dellemc.openmanage.plugins.module_utils.omevv import RestOMEVV, OMEVVAnsibleModule +from ansible_collections.dellemc.openmanage.plugins.module_utils.omevv_firmware_utils import OMEVVFirmwareProfile ODATA_REGEX = "(.*?)@odata" ODATA = "@odata.id" XML_EXT = ".xml" GZ_EXT = ".gz" XML_GZ_EXT = ".xml.gz" -HTTP_PATH = "http://" +HTTP_PATH = "http" HTTPS_PATH = "https://" MESSAGE_EXTENDED_INFO = "@Message.ExtendedInfo" TEST_CONNECTION_URI = "/RepositoryProfiles/TestConnection" PROFILE_URI = "/RepositoryProfiles" SUCCESS_CREATION_MSG = "Successfully created the OMEVV firmware repository profile." -SAME_PROFILE_MSG = "Firmware repository profile with name {profile_name} already exists." FAILED_CREATION_MSG = "Unable to create the OMEVV firmware repository profile." SUCCESS_MODIFY_MSG = "Successfully modified the OMEVV firmware repository profile." FAILED_MODIFY_MSG = "Unable to modify the OMEVV firmware repository profile as the details are same." @@ -173,13 +172,17 @@ class FirmwareRepositoryProfile: def __init__(self, module, rest_obj): self.module = module self.obj = rest_obj + self.omevv_profile_obj = OMEVVFirmwareProfile(self.obj, self.module) def get_payload_details(self): payload = {} payload["profileName"] = self.module.params.get('name') payload["protocolType"] = self.module.params.get('protocol_type').upper() payload["sharePath"] = self.module.params.get('catalog_path') - payload["description"] = self.module.params.get('description') + if self.module.params.get('description') is None: + payload["description"] = "" + else: + payload["description"] = self.module.params.get('description') payload["profileType"] = "Firmware" payload["shareCredential"] = { "username": self.module.params.get('share_username'), @@ -212,17 +215,17 @@ def get_modify_payload_details(self): def test_connection(self): payload = self.form_conn_payload() - resp = self.obj.invoke_request("POST", TEST_CONNECTION_URI, payload) + resp = self.omevv_profile_obj.test_connection(payload) if resp.success: return True else: self.module.exit_json(msg=FAILED_CONN_MSG, failed=True) def get_firmware_repository_profile(self): - res = FirmwareRepositoryProfile.execute(self) - if res: - resp = self.obj.invoke_request("GET", PROFILE_URI) - data = resp.json_data + # res = FirmwareRepositoryProfile.execute(self) + # if res: + resp = self.omevv_profile_obj.get_firmware_repository_profile() + data = resp.json_data return data def search_profile_name(self, data, profile_name): @@ -255,41 +258,54 @@ class CreateFirmwareRepositoryProfile(FirmwareRepositoryProfile): def __init__(self, module, rest_obj): self.module = module self.obj = rest_obj + self.omevv_profile_obj = OMEVVFirmwareProfile(self.obj, self.module) + + def diff_mode_check(self, payload): + diff = {} + diff = dict( + before={}, + after=payload + ) + return diff def create_firmware_repository_profile(self): diff = {} payload = self.get_payload_details() res = FirmwareRepositoryProfile.execute(self) if res: - if self.module._diff: - diff = dict( - before={}, - after=payload - ) - self.module.exit_json(changed=True, diff=diff) - resp = self.obj.invoke_request("POST", PROFILE_URI, payload) + diff = self.diff_mode_check(payload) + resp = self.omevv_profile_obj.create_firmware_repository_profile(payload) if resp.success: - self.module.exit_json(msg=SUCCESS_CREATION_MSG, changed=True) + profile_resp = self.omevv_profile_obj.get_specific_firmware_repository_profile(resp.json_data) + if self.module._diff: + self.module.exit_json(msg=SUCCESS_CREATION_MSG, profile_info=profile_resp.json_data, diff=diff, changed=True) + self.module.exit_json(msg=SUCCESS_CREATION_MSG, profile_info=profile_resp.json_data, changed=True) else: self.module.exit_json(msg=FAILED_CREATION_MSG, failed=True) def execute(self): + payload = self.get_payload_details() result = self.get_firmware_repository_profile() profile = self.module.params.get('name') new_profile = self.module.params.get('new_name') profile_exists = self.search_profile_name(result, profile) + if not profile_exists and self.module.check_mode and self.module._diff: + diff = self.diff_mode_check(payload) + self.module.exit_json(msg=CHANGES_FOUND_MSG, diff=diff, changed=True) if not profile_exists and self.module.check_mode: self.module.exit_json(msg=CHANGES_FOUND_MSG, changed=True) if not profile_exists and not self.module.check_mode: result = self.create_firmware_repository_profile() return result + if profile_exists and self.module._diff and not new_profile: + self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False, diff={"before": {}, "after": {}}) if profile_exists and self.module.check_mode and not new_profile: self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False) if profile_exists and new_profile: omevv_obj = ModifyFirmwareRepositoryProfile(self.module, self.obj) omevv_obj.execute() else: - self.module.exit_json(msg=SAME_PROFILE_MSG.format(profile_name=profile), skipped=True) + self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False) class ModifyFirmwareRepositoryProfile(FirmwareRepositoryProfile): @@ -297,11 +313,13 @@ class ModifyFirmwareRepositoryProfile(FirmwareRepositoryProfile): def __init__(self, module, rest_obj): self.module = module self.obj = rest_obj + self.omevv_profile_obj = OMEVVFirmwareProfile(self.obj, self.module) def diff_check(self, api_response, module_response): diff = {} del module_response["protocolType"] del module_response["shareCredential"] + api_response["sharePath"] = api_response["sharePath"] + '/' + api_response["fileName"] for key in module_response.keys(): if key not in api_response or api_response[key] != module_response[key]: diff[key] = module_response[key] @@ -314,40 +332,49 @@ def trim_api_response(self, api_response): trimmed_resp["description"] = api_response["description"] return trimmed_resp - def modify_firmware_repository_profile(self, api_response): + def diff_mode_check(self, payload, api_response): diff = {} - MODIFY_PROFILE_URI = PROFILE_URI + "/" + str(api_response["id"]) + del payload["shareCredential"] + payload["profileName"] = self.module.params.get('new_name') + if self.module._diff: + diff = dict( + before=self.diff_check(api_response, payload), + after=payload + ) + return diff + + def modify_firmware_repository_profile(self, api_response): payload = self.get_modify_payload_details() res = FirmwareRepositoryProfile.execute(self) if res: - if self.module._diff: - del payload["shareCredential"] - diff = dict( - before=self.trim_api_response(api_response), - after=payload - ) - self.module.exit_json(changed=True, diff=diff) - resp = self.obj.invoke_request("PUT", MODIFY_PROFILE_URI, payload) + diff = self.diff_mode_check(payload, self.trim_api_response(api_response)) + resp = self.omevv_profile_obj.modify_firmware_repository_profile(api_response["id"], payload) if resp.success: - self.module.exit_json(msg=SUCCESS_MODIFY_MSG, changed=True) + profile_resp = self.omevv_profile_obj.get_specific_firmware_repository_profile(api_response["id"]) + if self.module._diff: + self.module.exit_json(msg=SUCCESS_MODIFY_MSG, profile_info=profile_resp.json_data, diff=diff, changed=True) + self.module.exit_json(msg=SUCCESS_MODIFY_MSG, profile_info=profile_resp.json_data, changed=True) else: self.module.exit_json(msg=FAILED_MODIFY_MSG, failed=True) def execute(self): + payload = self.get_modify_payload_details() result = self.get_firmware_repository_profile() profile = self.module.params.get('name') api_response = self.search_profile_name(result, profile) module_response = self.get_payload_details() + module_response["profileName"] = self.module.params.get('new_name') diff = self.diff_check(api_response, module_response) + if diff and self.module.check_mode and self.module._diff: + diff = self.diff_mode_check(payload, self.trim_api_response(api_response)) + self.module.exit_json(msg=CHANGES_FOUND_MSG, diff=diff, changed=True) if diff and self.module.check_mode: self.module.exit_json(msg=CHANGES_FOUND_MSG, changed=True) if diff and not self.module.check_mode: result = self.modify_firmware_repository_profile(api_response) return result - if not diff and self.module.check_mode: - self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False) else: - self.module.exit_json(msg=SAME_PROFILE_MSG.format(profile_name=profile), skipped=True) + self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False) class DeleteFirmwareRepositoryProfile(FirmwareRepositoryProfile): @@ -355,32 +382,46 @@ class DeleteFirmwareRepositoryProfile(FirmwareRepositoryProfile): def __init__(self, module, rest_obj): self.module = module self.obj = rest_obj + self.omevv_profile_obj = OMEVVFirmwareProfile(self.obj, self.module) + + def diff_mode_check(self, payload): + diff = {} + diff_dict = {} + diff_dict["profileName"] = payload["profileName"] + diff_dict["description"] = payload["description"] + diff_dict["profileType"] = payload["profileType"] + diff_dict["sharePath"] = payload["sharePath"] + diff_dict["protocolType"] = payload["protocolType"] + if self.module._diff: + diff = dict( + before=diff_dict, + after={} + ) + return diff def delete_firmware_repository_profile(self, api_response): diff = {} - payload = self.get_payload_details() - DELETE_PROFILE_URI = PROFILE_URI + "/" + str(api_response["id"]) - res = FirmwareRepositoryProfile.execute(self) - if res: + diff = self.diff_mode_check(api_response) + resp = self.omevv_profile_obj.delete_firmware_repository_profile(api_response["id"]) + if resp.success: if self.module._diff: - diff = dict( - before=payload, - after={} - ) - self.module.exit_json(changed=True, diff=diff) - resp = self.obj.invoke_request("DELETE", DELETE_PROFILE_URI) - if resp.success: - self.module.exit_json(msg=SUCCESS_DELETION_MSG, changed=True) - else: - self.module.exit_json(msg=FAILED_DELETION_MSG, failed=True) + self.module.exit_json(msg=SUCCESS_DELETION_MSG, profile_info={}, diff=diff, changed=True) + self.module.exit_json(msg=SUCCESS_DELETION_MSG, profile_info={}, changed=True) + else: + self.module.exit_json(msg=FAILED_DELETION_MSG, failed=True) def execute(self): result = self.get_firmware_repository_profile() profile = self.module.params.get('name') api_response = self.search_profile_name(result, profile) profile_exists = self.search_profile_name(result, profile) + if profile_exists and self.module.check_mode and self.module._diff: + diff = self.diff_mode_check(api_response) + self.module.exit_json(msg=CHANGES_FOUND_MSG, diff=diff, changed=True) if not profile_exists and self.module.check_mode: self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False) + if not profile_exists and not self.module.check_mode and self.module._diff: + self.module.exit_json(msg=PROFILE_NOT_FOUND_MSG.format(profile_name=profile), diff={"before": {}, "after": {}}, profile_info={}, changed=False) if not profile_exists and not self.module.check_mode: self.module.exit_json(msg=PROFILE_NOT_FOUND_MSG.format(profile_name=profile), changed=False) if profile_exists and not self.module.check_mode: @@ -405,11 +446,11 @@ def main(): module = OMEVVAnsibleModule( argument_spec=argument_spec, required_if=[ - ["state", 'present', ("name", "catalog_path", "description", "protocol_type")], - ["protocol_type", "NFS", ("catalog_path", "share_domain")], - ["protocol_type", "CIFS", ("catalog_path", "share_username", "share_password", "share_domain")], - ["protocol_type", "HTTP", ("catalog_path", "share_domain")], - ["protocol_type", "HTTPS", ("catalog_path", "share_username", "share_password", "share_domain")], + ["state", 'present', ("name", "catalog_path", "protocol_type")], + ["protocol_type", "NFS", ("catalog_path",)], + ["protocol_type", "CIFS", ("catalog_path", "share_username", "share_password")], + ["protocol_type", "HTTP", ("catalog_path",)], + ["protocol_type", "HTTPS", ("catalog_path",)], ["state", 'absent', ("name",)] ], supports_check_mode=True) @@ -424,8 +465,8 @@ def main(): error_info = json.load(err) code = error_info.get('errorCode') message = error_info.get('message') - if '18001' in code: - module.exit_json(msg=message, skipped=True) + if '18001' in code and module.check_mode: + module.exit_json(msg=CHANGES_NOT_FOUND_MSG) if '500' in code: module.exit_json(msg=message, skipped=True) module.exit_json(msg=message, error_info=error_info, failed=True) diff --git a/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py b/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py new file mode 100644 index 000000000..e3ad0a7b7 --- /dev/null +++ b/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py @@ -0,0 +1,242 @@ +# -*- coding: utf-8 -*- + +# +# Dell OpenManage Ansible Modules +# Version 9.8.0 +# Copyright (C) 2024 Dell Inc. or its subsidiaries. All Rights Reserved. + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +import pytest +import json +from ansible_collections.dellemc.openmanage.plugins.modules import omevv_firmware_repository_profile +from ansible.module_utils.six.moves.urllib.error import HTTPError, URLError +from ansible.module_utils.urls import ConnectionError, SSLValidationError +from ansible_collections.dellemc.openmanage.tests.unit.plugins.modules.common import FakeAnsibleModule +from io import StringIO +from mock import MagicMock +from ansible.module_utils._text import to_text + +MODULE_PATH = 'ansible_collections.dellemc.openmanage.plugins.modules.omevv_firmware_repository_profile.' +SUCCESS_MSG = "Successfully retrieved the firmware repository profile information." +NO_PROFILE_MSG = "Unable to complete the operation because the '{profile_name}' is not a valid 'profile_name'." +FAILED_MSG = "Unable to fetch the firmware repository profile information." +INVOKE_REQ_KEY = "RestOMEVV.invoke_request" +GET_PROFILE_INFO_KEY = "OMEVVFirmwareProfile.get_firmware_repository_profile" +PERFORM_OPERATION_KEY = "FirmwareRepositoryProfile.execute" +HTTP_ERROR = "http error message" +HTTP_ERROR_URL = 'https://testhost.com' +RETURN_TYPE = "application/json" + + +class TestFirmwareRepositoryProfile(FakeAnsibleModule): + module = omevv_firmware_repository_profile + + @pytest.fixture + def omevv_firmware_repository_profile_mock(self): + omevv_obj = MagicMock() + return omevv_obj + + @pytest.fixture + def omevv_connection_firmware_repository_profile(self, mocker, omevv_firmware_repository_profile_mock): + omevv_conn_mock = mocker.patch(MODULE_PATH + 'RestOMEVV', + return_value=omevv_firmware_repository_profile_mock) + omevv_conn_mock.return_value.__enter__.return_value = omevv_firmware_repository_profile_mock + return omevv_conn_mock + + def test_connection(self, omevv_connection_firmware_repository_profile, omevv_default_args, mocker): + payload = { + "profileName": "test", + "description": "Test6", + "protocolType": "HTTPS", + "profileType": "Firmware", + "sharePath": "https://downloads.dell.com////catalog/catalog.xml.gz", + "catalogPath": "https://downloads.dell.com////catalog/catalog.xml.gz", + "checkCertificate": False} + mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.FirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) + result = obj.test_connection() + assert result == True + + def test_get_firmware_repository_profile(self, omevv_connection_firmware_repository_profile, omevv_default_args, mocker): + obj = MagicMock() + sample_resp = [ + { + "id": 1000, + "profileName": "Dell Default Catalog", + "description": "Latest Firmware From Dell", + "profileType": "Firmware", + "sharePath": "https://downloads.dell.com//catalog/catalog.xml.gz", + "fileName": "catalog.xml", + "status": "Success", + "factoryCreated": True, + "factoryType": "Default", + "catalogCreatedDate": "2024-08-27T01:58:10Z", + "catalogLastChecked": "2024-09-09T19:30:16Z", + "checkCertificate": "", + "protocolType": "HTTPS", + "createdBy": "OMEVV Default", + "modifiedBy": "", + "owner": "OMEVV" + } + ] + obj.json_data = sample_resp + mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.execute', return_value=True) + mocker.patch(MODULE_PATH + GET_PROFILE_INFO_KEY, return_value=obj) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.FirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) + result = obj.get_firmware_repository_profile() + assert result == sample_resp + + def test_search_profile_name(self, omevv_connection_firmware_repository_profile, omevv_default_args): + data = [ + { + "id": 1000, + "profileName": "Dell Default Catalog", + "description": "Latest Firmware From Dell", + "profileType": "Firmware", + "sharePath": "https://downloads.dell.com//catalog/catalog.xml.gz", + "fileName": "catalog.xml", + "status": "Success", + "factoryCreated": True, + "factoryType": "Default", + "catalogCreatedDate": "2024-08-27T01:58:10Z", + "catalogLastChecked": "2024-09-09T19:30:16Z", + "checkCertificate": "", + "protocolType": "HTTPS", + "createdBy": "OMEVV Default", + "modifiedBy": "", + "owner": "OMEVV" + } + ] + # Scenario 1: When profile name is present in the list + profile_name = "Dell Default Catalog" + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.FirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) + result = obj.search_profile_name(data, profile_name) + assert result == data[0] + + # Scenario 2: When profile name is not present in the list + profile_name = "Dell" + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.FirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) + result = obj.search_profile_name(data, profile_name) + assert result == {} + + def test_validate_catalog_path(self, omevv_connection_firmware_repository_profile, omevv_default_args): + protocol_type = "HTTPS" + # Scenario 1: When catalog path is valid + catalog_path = "https://downloads.dell.com//catalog/catalog.xml.gz" + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.FirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) + obj.validate_catalog_path(protocol_type, catalog_path) + + # Scenario 2: When catalog path is not valid + protocol_type = "HTTPS" + catalog_path = "https://downloads.dell.com//catalog/catalog" + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.FirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) + assert obj.validate_catalog_path(protocol_type, catalog_path) is None + + +class TestCreateFirmwareRepositoryProfile(FakeAnsibleModule): + module = omevv_firmware_repository_profile + + @pytest.fixture + def omevv_firmware_repository_profile_mock(self): + omevv_obj = MagicMock() + return omevv_obj + + @pytest.fixture + def omevv_connection_firmware_repository_profile(self, mocker, omevv_firmware_repository_profile_mock): + omevv_conn_mock = mocker.patch(MODULE_PATH + 'RestOMEVV', + return_value=omevv_firmware_repository_profile_mock) + omevv_conn_mock.return_value.__enter__.return_value = omevv_firmware_repository_profile_mock + return omevv_conn_mock + + def test_create_firmware_repository_profile(self, omevv_connection_firmware_repository_profile, omevv_default_args, mocker): + obj = MagicMock() + # Scenario 1: When creation is success + obj.success = True + payload = { + "profileName": "test", + "description": "Test6", + "protocolType": "HTTPS", + "profileType": "Firmware", + "sharePath": "https://downloads.dell.com////catalog/catalog.xml.gz"} + mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.execute', return_value=True) + mocker.patch(MODULE_PATH + GET_PROFILE_INFO_KEY, return_value=obj) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.CreateFirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) + obj.create_firmware_repository_profile() + + # Scenario 2: When creation is failed + obj2 = MagicMock() + obj2.status_code = 400 + mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.execute', return_value=True) + mocker.patch(MODULE_PATH + GET_PROFILE_INFO_KEY, return_value=obj2) + f_module = self.get_module_mock( + params=omevv_default_args) + obj2 = self.module.CreateFirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) + obj2.create_firmware_repository_profile() + + @pytest.mark.parametrize("exc_type", + [URLError, HTTPError, SSLValidationError, ConnectionError, TypeError, ValueError]) + def test_omevv_firmware_repository_profile_main_exception_handling_case(self, exc_type, mocker, omevv_default_args, + omevv_connection_firmware_repository_profile, omevv_firmware_repository_profile_mock): + omevv_firmware_repository_profile_mock.status_code = 400 + omevv_firmware_repository_profile_mock.success = False + json_str = to_text(json.dumps({"errorCode": "501", "message": "Error"})) + omevv_default_args.update({'state': 'absent', 'name': 'test'}) + if exc_type in [HTTPError, SSLValidationError]: + mocker.patch(MODULE_PATH + PERFORM_OPERATION_KEY, + side_effect=exc_type(HTTP_ERROR_URL, 400, + HTTP_ERROR, + {"accept-type": RETURN_TYPE}, + StringIO(json_str))) + else: + mocker.patch(MODULE_PATH + PERFORM_OPERATION_KEY, + side_effect=exc_type('test')) + result = self._run_module(omevv_default_args) + if exc_type == URLError: + assert result['unreachable'] is True + else: + assert result['failed'] is True + assert 'msg' in result + + # Scenario 1: When errorCode is 18001 + error_string = to_text(json.dumps({'errorCode': '18001', 'message': "Error"})) + if exc_type in [HTTPError]: + mocker.patch(MODULE_PATH + PERFORM_OPERATION_KEY, + side_effect=exc_type(HTTP_ERROR_URL, 400, + HTTP_ERROR, + {"accept-type": RETURN_TYPE}, + StringIO(error_string))) + res_out = self._run_module(omevv_default_args) + assert 'msg' in res_out + + # Scenario 2: When errorCode is 500 + error_string = to_text(json.dumps({'errorCode': '500', 'message': "Error"})) + if exc_type in [HTTPError, SSLValidationError]: + mocker.patch(MODULE_PATH + PERFORM_OPERATION_KEY, + side_effect=exc_type(HTTP_ERROR_URL, 400, + HTTP_ERROR, + {"accept-type": RETURN_TYPE}, + StringIO(error_string))) + res_out = self._run_module(omevv_default_args) + assert 'msg' in res_out From 8f5d18a48cfa88bc87b0cd924ebaec11445ee29b Mon Sep 17 00:00:00 2001 From: Shivam Sharma Date: Thu, 17 Oct 2024 00:36:08 -0400 Subject: [PATCH 04/10] refactored code --- plugins/module_utils/omevv_firmware_utils.py | 158 +++++++++++++++++- .../omevv_firmware_repository_profile.py | 125 ++++---------- .../test_omevv_firmware_repository_profile.py | 92 ++-------- 3 files changed, 198 insertions(+), 177 deletions(-) diff --git a/plugins/module_utils/omevv_firmware_utils.py b/plugins/module_utils/omevv_firmware_utils.py index 4a75ba9df..234ebcd57 100644 --- a/plugins/module_utils/omevv_firmware_utils.py +++ b/plugins/module_utils/omevv_firmware_utils.py @@ -30,14 +30,156 @@ __metaclass__ = type +XML_EXT = ".xml" +GZ_EXT = ".gz" +XML_GZ_EXT = ".xml.gz" +HTTP_PATH = "http" +HTTPS_PATH = "https://" PROFILE_URI = "/RepositoryProfiles" TEST_CONNECTION_URI = "/RepositoryProfiles/TestConnection" - + class OMEVVFirmwareProfile: - def __init__(self, omevv, module): + def __init__(self, omevv): self.omevv = omevv - self.module = module + + def get_payload_details(self, **kwargs): + """ + Returns a dictionary containing the payload details. + + Args: + **kwargs: The keyword arguments. + - name (str): The name of the profile. + - protocol_type (str): The protocol type. + - catalog_path (str): The share path. + - description (str, optional): The description. + - share_username (str): The share username. + - share_password (str): The share password. + - share_domain (str): The share domain. + + Returns: + dict: The payload details. + """ + payload = {} + payload["profileName"] = kwargs.get('name') + payload["protocolType"] = kwargs.get('protocol_type') + payload["sharePath"] = kwargs.get('catalog_path') + if kwargs.get('description') is None: + payload["description"] = "" + else: + payload["description"] = kwargs.get('description') + payload["profileType"] = "Firmware" + payload["shareCredential"] = { + "username": kwargs.get('share_username'), + "password": kwargs.get('share_password'), + "domain": kwargs.get('share_dommain') + } + return payload + + def form_conn_payload(self, **kwargs): + """ + Generates a payload for forming a test connection. + + Args: + **kwargs: Keyword arguments. + - name (str): The name of the profile. + - protocol_type (str): The protocol type. + - catalog_path (str): The share path. + - description (str, optional): The description. + - share_username (str): The share username. + - share_password (str): The share password. + - share_dommain (str): The share domain. + + Returns: + dict: The payload for forming a connection. + - catalogPath (str): The share path. + - checkCertificate (bool): Whether to check the certificate. + + """ + payload = self.get_payload_details(**kwargs) + del payload["profileName"] + del payload["sharePath"] + payload["catalogPath"] = kwargs.get('catalog_path') + del payload["description"] + del payload["profileType"] + payload["checkCertificate"] = False + return payload + + def get_modify_payload_details(self, **kwargs): + """ + Returns a dictionary containing the payload details for modifying a firmware profile. + + Args: + **kwargs: Keyword arguments. + - new_name (str): The new name of the profile. + - catalog_path (str): The new share path. + - description (str): The new description. + - share_username (str): The new share username. + - share_password (str): The new share password. + - share_dommain (str): The new share domain. + + Returns: + dict: The payload details. + - profileName (str): The new name of the profile. + - sharePath (str): The new share path. + - description (str): The new description. + - shareCredential (dict): The new share credentials. + - username (str): The new share username. + - password (str): The new share password. + - domain (str): The new share domain. + """ + payload = {} + payload["profileName"] = kwargs.get('new_name') + payload["sharePath"] = kwargs.get('catalog_path') + payload["description"] = kwargs.get('description') + payload["shareCredential"] = { + "username": kwargs.get('share_username'), + "password": kwargs.get('share_password'), + "domain": kwargs.get('share_dommain') + } + return payload + + def search_profile_name(self, data, profile_name): + """ + Searches for a profile with the given name in the provided data. + + Args: + data (list): A list of dictionaries representing profiles. + profile_name (str): The name of the profile to search for. + + Returns: + dict: The dictionary representing the profile if found, or an empty dictionary if not found. + """ + for d in data: + if d.get('profileName') == profile_name: + return d + return {} + + def validate_catalog_path(self, protocol_type, catalog_path): + """ + Validates the catalog path based on the protocol type. + + Args: + protocol_type (str): The type of protocol used for the catalog path. + catalog_path (str): The path to the catalog. + + Raises: + SystemExit: If the catalog path is invalid. + + Returns: + None + """ + protocol_mapping = { + 'CIFS': (lambda path: path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT)), + 'NFS': (lambda path: path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT)), + 'HTTP': (lambda path: path.startswith(HTTP_PATH) and (path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT))), + "HTTPS": (lambda path: path.startswith(HTTPS_PATH) and (path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT))) + } + validator = protocol_mapping.get(protocol_type) + if validator is None: + self.module.exit_json(msg="Invalid catalog_path", failed=True) + if not validator(catalog_path): + self.module.exit_json(msg="Invalid catalog_path", failed=True) def test_connection(self, payload): """ @@ -46,7 +188,7 @@ def test_connection(self, payload): """ resp = self.omevv.invoke_request("POST", TEST_CONNECTION_URI, payload) return resp - + def get_firmware_repository_profile(self): """ Retrieves all firmware repository profile Information. @@ -54,15 +196,15 @@ def get_firmware_repository_profile(self): """ resp = self.omevv.invoke_request("GET", PROFILE_URI) return resp - - def get_specific_firmware_repository_profile(self, profile_id): + + def get_firmware_repository_profile_by_id(self, profile_id): """ Retrieves all firmware repository profile Information. """ resp = self.omevv.invoke_request("GET", PROFILE_URI + "/" + str(profile_id)) return resp - + def create_firmware_repository_profile(self, payload): """ Creates a firmware repository profile. @@ -70,7 +212,7 @@ def create_firmware_repository_profile(self, payload): """ resp = self.omevv.invoke_request("POST", PROFILE_URI, payload) return resp - + def modify_firmware_repository_profile(self, profile_id, payload): """ Modifies a firmware repository profile. diff --git a/plugins/modules/omevv_firmware_repository_profile.py b/plugins/modules/omevv_firmware_repository_profile.py index d6cf44a10..aef64cdde 100644 --- a/plugins/modules/omevv_firmware_repository_profile.py +++ b/plugins/modules/omevv_firmware_repository_profile.py @@ -147,11 +147,6 @@ ODATA_REGEX = "(.*?)@odata" ODATA = "@odata.id" -XML_EXT = ".xml" -GZ_EXT = ".gz" -XML_GZ_EXT = ".xml.gz" -HTTP_PATH = "http" -HTTPS_PATH = "https://" MESSAGE_EXTENDED_INFO = "@Message.ExtendedInfo" TEST_CONNECTION_URI = "/RepositoryProfiles/TestConnection" PROFILE_URI = "/RepositoryProfiles" @@ -172,49 +167,19 @@ class FirmwareRepositoryProfile: def __init__(self, module, rest_obj): self.module = module self.obj = rest_obj - self.omevv_profile_obj = OMEVVFirmwareProfile(self.obj, self.module) - - def get_payload_details(self): - payload = {} - payload["profileName"] = self.module.params.get('name') - payload["protocolType"] = self.module.params.get('protocol_type').upper() - payload["sharePath"] = self.module.params.get('catalog_path') - if self.module.params.get('description') is None: - payload["description"] = "" - else: - payload["description"] = self.module.params.get('description') - payload["profileType"] = "Firmware" - payload["shareCredential"] = { - "username": self.module.params.get('share_username'), - "password": self.module.params.get('share_password'), - "domain": self.module.params.get('share_dommain') - } - return payload - - def form_conn_payload(self): - payload = self.get_payload_details() - del payload["profileName"] - del payload["sharePath"] - payload["catalogPath"] = self.module.params.get('catalog_path') - del payload["description"] - del payload["profileType"] - payload["checkCertificate"] = False - return payload - - def get_modify_payload_details(self): - payload = {} - payload["profileName"] = self.module.params.get('new_name') - payload["sharePath"] = self.module.params.get('catalog_path') - payload["description"] = self.module.params.get('description') - payload["shareCredential"] = { - "username": self.module.params.get('share_username'), - "password": self.module.params.get('share_password'), - "domain": self.module.params.get('share_dommain') - } - return payload + self.omevv_profile_obj = OMEVVFirmwareProfile(self.obj) + + def module_params(self): + module_params = {} + params_list = ['name', 'new_name', 'catalog_path', 'description', 'protocol_type', 'share_username', 'share_password', 'share_domain'] + for param in params_list: + value = self.module.params.get(param) + module_params[param] = value if value is not None else "" + return module_params def test_connection(self): - payload = self.form_conn_payload() + module_params = self.module_params() + payload = self.omevv_profile_obj.form_conn_payload(**module_params) resp = self.omevv_profile_obj.test_connection(payload) if resp.success: return True @@ -222,33 +187,12 @@ def test_connection(self): self.module.exit_json(msg=FAILED_CONN_MSG, failed=True) def get_firmware_repository_profile(self): - # res = FirmwareRepositoryProfile.execute(self) - # if res: resp = self.omevv_profile_obj.get_firmware_repository_profile() data = resp.json_data return data - def search_profile_name(self, data, profile_name): - for d in data: - if d.get('profileName') == profile_name: - return d - return {} - - def validate_catalog_path(self, protocol_type, catalog_path): - protocol_mapping = { - 'CIFS': (lambda path: path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT)), - 'NFS': (lambda path: path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT)), - 'HTTP': (lambda path: path.startswith(HTTP_PATH) and (path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT))), - "HTTPS": (lambda path: path.startswith(HTTPS_PATH) and (path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT))) - } - validator = protocol_mapping.get(protocol_type) - if validator is None: - self.module.exit_json(msg="Invalid catalog_path", failed=True) - if not validator(catalog_path): - self.module.exit_json(msg="Invalid catalog_path", failed=True) - def execute(self): - self.validate_catalog_path(self.module.params.get('protocol_type'), self.module.params.get('catalog_path')) + self.omevv_profile_obj.validate_catalog_path(self.module.params.get('protocol_type'), self.module.params.get('catalog_path')) result = self.test_connection() return result @@ -258,7 +202,7 @@ class CreateFirmwareRepositoryProfile(FirmwareRepositoryProfile): def __init__(self, module, rest_obj): self.module = module self.obj = rest_obj - self.omevv_profile_obj = OMEVVFirmwareProfile(self.obj, self.module) + super().__init__(module, rest_obj) def diff_mode_check(self, payload): diff = {} @@ -270,13 +214,14 @@ def diff_mode_check(self, payload): def create_firmware_repository_profile(self): diff = {} - payload = self.get_payload_details() + module_params = self.module_params() + payload = self.omevv_profile_obj.get_payload_details(**module_params) res = FirmwareRepositoryProfile.execute(self) if res: diff = self.diff_mode_check(payload) resp = self.omevv_profile_obj.create_firmware_repository_profile(payload) if resp.success: - profile_resp = self.omevv_profile_obj.get_specific_firmware_repository_profile(resp.json_data) + profile_resp = self.omevv_profile_obj.get_firmware_repository_profile_by_id(resp.json_data) if self.module._diff: self.module.exit_json(msg=SUCCESS_CREATION_MSG, profile_info=profile_resp.json_data, diff=diff, changed=True) self.module.exit_json(msg=SUCCESS_CREATION_MSG, profile_info=profile_resp.json_data, changed=True) @@ -284,19 +229,19 @@ def create_firmware_repository_profile(self): self.module.exit_json(msg=FAILED_CREATION_MSG, failed=True) def execute(self): - payload = self.get_payload_details() + module_params = self.module_params() + payload = self.omevv_profile_obj.get_payload_details(**module_params) result = self.get_firmware_repository_profile() profile = self.module.params.get('name') new_profile = self.module.params.get('new_name') - profile_exists = self.search_profile_name(result, profile) + profile_exists = self.omevv_profile_obj.search_profile_name(result, profile) if not profile_exists and self.module.check_mode and self.module._diff: diff = self.diff_mode_check(payload) self.module.exit_json(msg=CHANGES_FOUND_MSG, diff=diff, changed=True) if not profile_exists and self.module.check_mode: self.module.exit_json(msg=CHANGES_FOUND_MSG, changed=True) if not profile_exists and not self.module.check_mode: - result = self.create_firmware_repository_profile() - return result + self.create_firmware_repository_profile() if profile_exists and self.module._diff and not new_profile: self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False, diff={"before": {}, "after": {}}) if profile_exists and self.module.check_mode and not new_profile: @@ -313,7 +258,7 @@ class ModifyFirmwareRepositoryProfile(FirmwareRepositoryProfile): def __init__(self, module, rest_obj): self.module = module self.obj = rest_obj - self.omevv_profile_obj = OMEVVFirmwareProfile(self.obj, self.module) + super().__init__(module, rest_obj) def diff_check(self, api_response, module_response): diff = {} @@ -344,13 +289,14 @@ def diff_mode_check(self, payload, api_response): return diff def modify_firmware_repository_profile(self, api_response): - payload = self.get_modify_payload_details() + module_params = self.module_params() + payload = self.omevv_profile_obj.get_modify_payload_details(**module_params) res = FirmwareRepositoryProfile.execute(self) if res: diff = self.diff_mode_check(payload, self.trim_api_response(api_response)) resp = self.omevv_profile_obj.modify_firmware_repository_profile(api_response["id"], payload) if resp.success: - profile_resp = self.omevv_profile_obj.get_specific_firmware_repository_profile(api_response["id"]) + profile_resp = self.omevv_profile_obj.get_firmware_repository_profile_by_id(api_response["id"]) if self.module._diff: self.module.exit_json(msg=SUCCESS_MODIFY_MSG, profile_info=profile_resp.json_data, diff=diff, changed=True) self.module.exit_json(msg=SUCCESS_MODIFY_MSG, profile_info=profile_resp.json_data, changed=True) @@ -358,11 +304,12 @@ def modify_firmware_repository_profile(self, api_response): self.module.exit_json(msg=FAILED_MODIFY_MSG, failed=True) def execute(self): - payload = self.get_modify_payload_details() + module_params = self.module_params() + payload = self.omevv_profile_obj.get_modify_payload_details() result = self.get_firmware_repository_profile() profile = self.module.params.get('name') - api_response = self.search_profile_name(result, profile) - module_response = self.get_payload_details() + api_response = self.omevv_profile_obj.search_profile_name(result, profile) + module_response = self.omevv_profile_obj.get_payload_details(**module_params) module_response["profileName"] = self.module.params.get('new_name') diff = self.diff_check(api_response, module_response) if diff and self.module.check_mode and self.module._diff: @@ -371,8 +318,7 @@ def execute(self): if diff and self.module.check_mode: self.module.exit_json(msg=CHANGES_FOUND_MSG, changed=True) if diff and not self.module.check_mode: - result = self.modify_firmware_repository_profile(api_response) - return result + self.modify_firmware_repository_profile(api_response) else: self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False) @@ -382,7 +328,7 @@ class DeleteFirmwareRepositoryProfile(FirmwareRepositoryProfile): def __init__(self, module, rest_obj): self.module = module self.obj = rest_obj - self.omevv_profile_obj = OMEVVFirmwareProfile(self.obj, self.module) + super().__init__(module, rest_obj) def diff_mode_check(self, payload): diff = {} @@ -413,8 +359,8 @@ def delete_firmware_repository_profile(self, api_response): def execute(self): result = self.get_firmware_repository_profile() profile = self.module.params.get('name') - api_response = self.search_profile_name(result, profile) - profile_exists = self.search_profile_name(result, profile) + api_response = self.omevv_profile_obj.search_profile_name(result, profile) + profile_exists = self.omevv_profile_obj.search_profile_name(result, profile) if profile_exists and self.module.check_mode and self.module._diff: diff = self.diff_mode_check(api_response) self.module.exit_json(msg=CHANGES_FOUND_MSG, diff=diff, changed=True) @@ -423,10 +369,9 @@ def execute(self): if not profile_exists and not self.module.check_mode and self.module._diff: self.module.exit_json(msg=PROFILE_NOT_FOUND_MSG.format(profile_name=profile), diff={"before": {}, "after": {}}, profile_info={}, changed=False) if not profile_exists and not self.module.check_mode: - self.module.exit_json(msg=PROFILE_NOT_FOUND_MSG.format(profile_name=profile), changed=False) + self.module.exit_json(msg=PROFILE_NOT_FOUND_MSG.format(profile_name=profile), profile_info={}, changed=False) if profile_exists and not self.module.check_mode: - result = self.delete_firmware_repository_profile(api_response) - return result + self.delete_firmware_repository_profile(api_response) if profile_exists and self.module.check_mode: self.module.exit_json(msg=CHANGES_FOUND_MSG, changed=True) @@ -462,6 +407,8 @@ def main(): omevv_obj = DeleteFirmwareRepositoryProfile(module, rest_obj) omevv_obj.execute() except HTTPError as err: + if err.code == 500: + module.exit_json(msg=json.load(err), failed=True) error_info = json.load(err) code = error_info.get('errorCode') message = error_info.get('message') diff --git a/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py b/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py index e3ad0a7b7..14b871154 100644 --- a/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py +++ b/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py @@ -23,6 +23,7 @@ from ansible.module_utils._text import to_text MODULE_PATH = 'ansible_collections.dellemc.openmanage.plugins.modules.omevv_firmware_repository_profile.' +MODULE_UTILS_PATH = 'ansible_collections.dellemc.openmanage.plugins.module_utils.omevv_firmware_utils.' SUCCESS_MSG = "Successfully retrieved the firmware repository profile information." NO_PROFILE_MSG = "Unable to complete the operation because the '{profile_name}' is not a valid 'profile_name'." FAILED_MSG = "Unable to fetch the firmware repository profile information." @@ -32,6 +33,7 @@ HTTP_ERROR = "http error message" HTTP_ERROR_URL = 'https://testhost.com' RETURN_TYPE = "application/json" +SHARE_PATH = "https://downloads.dell.com//catalog/catalog.xml.gz" class TestFirmwareRepositoryProfile(FakeAnsibleModule): @@ -49,22 +51,6 @@ def omevv_connection_firmware_repository_profile(self, mocker, omevv_firmware_re omevv_conn_mock.return_value.__enter__.return_value = omevv_firmware_repository_profile_mock return omevv_conn_mock - def test_connection(self, omevv_connection_firmware_repository_profile, omevv_default_args, mocker): - payload = { - "profileName": "test", - "description": "Test6", - "protocolType": "HTTPS", - "profileType": "Firmware", - "sharePath": "https://downloads.dell.com////catalog/catalog.xml.gz", - "catalogPath": "https://downloads.dell.com////catalog/catalog.xml.gz", - "checkCertificate": False} - mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) - f_module = self.get_module_mock( - params=omevv_default_args) - obj = self.module.FirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) - result = obj.test_connection() - assert result == True - def test_get_firmware_repository_profile(self, omevv_connection_firmware_repository_profile, omevv_default_args, mocker): obj = MagicMock() sample_resp = [ @@ -73,7 +59,7 @@ def test_get_firmware_repository_profile(self, omevv_connection_firmware_reposit "profileName": "Dell Default Catalog", "description": "Latest Firmware From Dell", "profileType": "Firmware", - "sharePath": "https://downloads.dell.com//catalog/catalog.xml.gz", + "sharePath": SHARE_PATH, "fileName": "catalog.xml", "status": "Success", "factoryCreated": True, @@ -88,7 +74,7 @@ def test_get_firmware_repository_profile(self, omevv_connection_firmware_reposit } ] obj.json_data = sample_resp - mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.execute', return_value=True) + mocker.patch(MODULE_PATH + PERFORM_OPERATION_KEY, return_value=True) mocker.patch(MODULE_PATH + GET_PROFILE_INFO_KEY, return_value=obj) f_module = self.get_module_mock( params=omevv_default_args) @@ -96,60 +82,6 @@ def test_get_firmware_repository_profile(self, omevv_connection_firmware_reposit result = obj.get_firmware_repository_profile() assert result == sample_resp - def test_search_profile_name(self, omevv_connection_firmware_repository_profile, omevv_default_args): - data = [ - { - "id": 1000, - "profileName": "Dell Default Catalog", - "description": "Latest Firmware From Dell", - "profileType": "Firmware", - "sharePath": "https://downloads.dell.com//catalog/catalog.xml.gz", - "fileName": "catalog.xml", - "status": "Success", - "factoryCreated": True, - "factoryType": "Default", - "catalogCreatedDate": "2024-08-27T01:58:10Z", - "catalogLastChecked": "2024-09-09T19:30:16Z", - "checkCertificate": "", - "protocolType": "HTTPS", - "createdBy": "OMEVV Default", - "modifiedBy": "", - "owner": "OMEVV" - } - ] - # Scenario 1: When profile name is present in the list - profile_name = "Dell Default Catalog" - f_module = self.get_module_mock( - params=omevv_default_args) - obj = self.module.FirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) - result = obj.search_profile_name(data, profile_name) - assert result == data[0] - - # Scenario 2: When profile name is not present in the list - profile_name = "Dell" - f_module = self.get_module_mock( - params=omevv_default_args) - obj = self.module.FirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) - result = obj.search_profile_name(data, profile_name) - assert result == {} - - def test_validate_catalog_path(self, omevv_connection_firmware_repository_profile, omevv_default_args): - protocol_type = "HTTPS" - # Scenario 1: When catalog path is valid - catalog_path = "https://downloads.dell.com//catalog/catalog.xml.gz" - f_module = self.get_module_mock( - params=omevv_default_args) - obj = self.module.FirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) - obj.validate_catalog_path(protocol_type, catalog_path) - - # Scenario 2: When catalog path is not valid - protocol_type = "HTTPS" - catalog_path = "https://downloads.dell.com//catalog/catalog" - f_module = self.get_module_mock( - params=omevv_default_args) - obj = self.module.FirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) - assert obj.validate_catalog_path(protocol_type, catalog_path) is None - class TestCreateFirmwareRepositoryProfile(FakeAnsibleModule): module = omevv_firmware_repository_profile @@ -165,7 +97,7 @@ def omevv_connection_firmware_repository_profile(self, mocker, omevv_firmware_re return_value=omevv_firmware_repository_profile_mock) omevv_conn_mock.return_value.__enter__.return_value = omevv_firmware_repository_profile_mock return omevv_conn_mock - + def test_create_firmware_repository_profile(self, omevv_connection_firmware_repository_profile, omevv_default_args, mocker): obj = MagicMock() # Scenario 1: When creation is success @@ -175,9 +107,9 @@ def test_create_firmware_repository_profile(self, omevv_connection_firmware_repo "description": "Test6", "protocolType": "HTTPS", "profileType": "Firmware", - "sharePath": "https://downloads.dell.com////catalog/catalog.xml.gz"} - mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) - mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.execute', return_value=True) + "sharePath": SHARE_PATH} + mocker.patch(MODULE_UTILS_PATH + 'OMEVVFirmwareProfile.get_payload_details', return_value=payload) + mocker.patch(MODULE_PATH + PERFORM_OPERATION_KEY, return_value=True) mocker.patch(MODULE_PATH + GET_PROFILE_INFO_KEY, return_value=obj) f_module = self.get_module_mock( params=omevv_default_args) @@ -187,8 +119,8 @@ def test_create_firmware_repository_profile(self, omevv_connection_firmware_repo # Scenario 2: When creation is failed obj2 = MagicMock() obj2.status_code = 400 - mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) - mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.execute', return_value=True) + mocker.patch(MODULE_UTILS_PATH + 'OMEVVFirmwareProfile.get_payload_details', return_value=payload) + mocker.patch(MODULE_PATH + PERFORM_OPERATION_KEY, return_value=True) mocker.patch(MODULE_PATH + GET_PROFILE_INFO_KEY, return_value=obj2) f_module = self.get_module_mock( params=omevv_default_args) @@ -198,7 +130,7 @@ def test_create_firmware_repository_profile(self, omevv_connection_firmware_repo @pytest.mark.parametrize("exc_type", [URLError, HTTPError, SSLValidationError, ConnectionError, TypeError, ValueError]) def test_omevv_firmware_repository_profile_main_exception_handling_case(self, exc_type, mocker, omevv_default_args, - omevv_connection_firmware_repository_profile, omevv_firmware_repository_profile_mock): + omevv_firmware_repository_profile_mock): omevv_firmware_repository_profile_mock.status_code = 400 omevv_firmware_repository_profile_mock.success = False json_str = to_text(json.dumps({"errorCode": "501", "message": "Error"})) @@ -214,7 +146,7 @@ def test_omevv_firmware_repository_profile_main_exception_handling_case(self, ex side_effect=exc_type('test')) result = self._run_module(omevv_default_args) if exc_type == URLError: - assert result['unreachable'] is True + assert result['changed'] is False else: assert result['failed'] is True assert 'msg' in result From cda863a36593346e01be097d14f6863e9d3cbfba Mon Sep 17 00:00:00 2001 From: Shivam Sharma Date: Fri, 18 Oct 2024 00:23:14 -0400 Subject: [PATCH 05/10] reJIT-317494 fixed --- plugins/module_utils/omevv_firmware_utils.py | 149 +++++++----------- .../omevv_firmware_repository_profile.py | 100 ++++++++---- 2 files changed, 126 insertions(+), 123 deletions(-) diff --git a/plugins/module_utils/omevv_firmware_utils.py b/plugins/module_utils/omevv_firmware_utils.py index 234ebcd57..d5534bf91 100644 --- a/plugins/module_utils/omevv_firmware_utils.py +++ b/plugins/module_utils/omevv_firmware_utils.py @@ -43,100 +43,49 @@ class OMEVVFirmwareProfile: def __init__(self, omevv): self.omevv = omevv - def get_payload_details(self, **kwargs): - """ - Returns a dictionary containing the payload details. - - Args: - **kwargs: The keyword arguments. - - name (str): The name of the profile. - - protocol_type (str): The protocol type. - - catalog_path (str): The share path. - - description (str, optional): The description. - - share_username (str): The share username. - - share_password (str): The share password. - - share_domain (str): The share domain. - - Returns: - dict: The payload details. - """ + def get_create_payload_details(self, name, catalog_path, description, protocol_type, share_username, share_password, share_domain): payload = {} - payload["profileName"] = kwargs.get('name') - payload["protocolType"] = kwargs.get('protocol_type') - payload["sharePath"] = kwargs.get('catalog_path') - if kwargs.get('description') is None: + payload["profileName"] = name + payload["protocolType"] = protocol_type + payload["sharePath"] = catalog_path + if description is None: payload["description"] = "" else: - payload["description"] = kwargs.get('description') + payload["description"] = description payload["profileType"] = "Firmware" payload["shareCredential"] = { - "username": kwargs.get('share_username'), - "password": kwargs.get('share_password'), - "domain": kwargs.get('share_dommain') + "username": share_username, + "password": share_password, + "domain": share_domain } return payload - - def form_conn_payload(self, **kwargs): - """ - Generates a payload for forming a test connection. - - Args: - **kwargs: Keyword arguments. - - name (str): The name of the profile. - - protocol_type (str): The protocol type. - - catalog_path (str): The share path. - - description (str, optional): The description. - - share_username (str): The share username. - - share_password (str): The share password. - - share_dommain (str): The share domain. - - Returns: - dict: The payload for forming a connection. - - catalogPath (str): The share path. - - checkCertificate (bool): Whether to check the certificate. - - """ - payload = self.get_payload_details(**kwargs) - del payload["profileName"] - del payload["sharePath"] - payload["catalogPath"] = kwargs.get('catalog_path') - del payload["description"] - del payload["profileType"] - payload["checkCertificate"] = False + + def get_modify_payload_details(self, name, catalog_path, description, share_username, share_password, share_domain): + payload = {} + payload["profileName"] = name + payload["sharePath"] = catalog_path + if description is None: + payload["description"] = "" + else: + payload["description"] = description + payload["profileType"] = "Firmware" + payload["shareCredential"] = { + "username": share_username, + "password": share_password, + "domain": share_domain + } return payload - def get_modify_payload_details(self, **kwargs): - """ - Returns a dictionary containing the payload details for modifying a firmware profile. - - Args: - **kwargs: Keyword arguments. - - new_name (str): The new name of the profile. - - catalog_path (str): The new share path. - - description (str): The new description. - - share_username (str): The new share username. - - share_password (str): The new share password. - - share_dommain (str): The new share domain. - - Returns: - dict: The payload details. - - profileName (str): The new name of the profile. - - sharePath (str): The new share path. - - description (str): The new description. - - shareCredential (dict): The new share credentials. - - username (str): The new share username. - - password (str): The new share password. - - domain (str): The new share domain. - """ + def form_conn_payload(self, protocol_type, catalog_path, share_username, share_password, share_domain): payload = {} - payload["profileName"] = kwargs.get('new_name') - payload["sharePath"] = kwargs.get('catalog_path') - payload["description"] = kwargs.get('description') + payload["protocolType"] = protocol_type + payload["catalogPath"] = catalog_path payload["shareCredential"] = { - "username": kwargs.get('share_username'), - "password": kwargs.get('share_password'), - "domain": kwargs.get('share_dommain') + "username": share_username if share_username is not None else "", + "password": share_password if share_password is not None else "", + "domain": share_domain if share_domain is not None else "" } + payload["checkCertificate"] = False return payload def search_profile_name(self, data, profile_name): @@ -181,11 +130,12 @@ def validate_catalog_path(self, protocol_type, catalog_path): if not validator(catalog_path): self.module.exit_json(msg="Invalid catalog_path", failed=True) - def test_connection(self, payload): + def test_connection(self, protocol_type, catalog_path, share_username, share_password, share_domain): """ Tests the connection to the vCenter server. """ + payload = self.form_conn_payload(protocol_type, catalog_path, share_username, share_password, share_domain) resp = self.omevv.invoke_request("POST", TEST_CONNECTION_URI, payload) return resp @@ -205,21 +155,38 @@ def get_firmware_repository_profile_by_id(self, profile_id): resp = self.omevv.invoke_request("GET", PROFILE_URI + "/" + str(profile_id)) return resp - def create_firmware_repository_profile(self, payload): - """ - Creates a firmware repository profile. - - """ + def create_firmware_repository_profile(self, name, catalog_path, + description, protocol_type, + share_username, share_password, + share_domain): + err_msg = None + if name is None: + err_msg = "" + + payload = self.get_create_payload_details(name, catalog_path, + description, protocol_type, + share_username, share_password, + share_domain) resp = self.omevv.invoke_request("POST", PROFILE_URI, payload) - return resp + return resp, err_msg - def modify_firmware_repository_profile(self, profile_id, payload): + def modify_firmware_repository_profile(self, profile_id, name, catalog_path, + description, + share_username, share_password, + share_domain): """ Modifies a firmware repository profile. """ + err_msg = None + if name is None: + err_msg = "" + payload = self.get_modify_payload_details(name, catalog_path, + description, + share_username, share_password, + share_domain) resp = self.omevv.invoke_request("PUT", PROFILE_URI + "/" + str(profile_id), payload) - return resp + return resp, err_msg def delete_firmware_repository_profile(self, profile_id): """ diff --git a/plugins/modules/omevv_firmware_repository_profile.py b/plugins/modules/omevv_firmware_repository_profile.py index aef64cdde..abb453a80 100644 --- a/plugins/modules/omevv_firmware_repository_profile.py +++ b/plugins/modules/omevv_firmware_repository_profile.py @@ -140,10 +140,12 @@ } ''' import json +import time from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError from ansible.module_utils.urls import ConnectionError from ansible_collections.dellemc.openmanage.plugins.module_utils.omevv import RestOMEVV, OMEVVAnsibleModule from ansible_collections.dellemc.openmanage.plugins.module_utils.omevv_firmware_utils import OMEVVFirmwareProfile +from ansible.module_utils.common.dict_transformations import recursive_diff ODATA_REGEX = "(.*?)@odata" ODATA = "@odata.id" @@ -169,18 +171,31 @@ def __init__(self, module, rest_obj): self.obj = rest_obj self.omevv_profile_obj = OMEVVFirmwareProfile(self.obj) - def module_params(self): - module_params = {} - params_list = ['name', 'new_name', 'catalog_path', 'description', 'protocol_type', 'share_username', 'share_password', 'share_domain'] - for param in params_list: - value = self.module.params.get(param) - module_params[param] = value if value is not None else "" - return module_params + def get_payload_details(self): + payload = {} + payload["profileName"] = self.module.params.get('name') + payload["protocolType"] = self.module.params.get('protocol_type').upper() + payload["sharePath"] = self.module.params.get('catalog_path') + if self.module.params.get('description') is None: + payload["description"] = "" + else: + payload["description"] = self.module.params.get('description') + payload["profileType"] = "Firmware" + payload["shareCredential"] = { + "username": self.module.params.get('share_username'), + "password": self.module.params.get('share_password'), + "domain": self.module.params.get('share_dommain') + } + return payload def test_connection(self): - module_params = self.module_params() - payload = self.omevv_profile_obj.form_conn_payload(**module_params) - resp = self.omevv_profile_obj.test_connection(payload) + resp = self.omevv_profile_obj.test_connection( + protocol_type=self.module.params.get('protocol_type'), + catalog_path=self.module.params.get('catalog_path'), + share_username=self.module.params.get('share_username'), + share_password=self.module.params.get('share_password'), + share_domain=self.module.params.get('share_domain') + ) if resp.success: return True else: @@ -206,6 +221,7 @@ def __init__(self, module, rest_obj): def diff_mode_check(self, payload): diff = {} + del payload["shareCredential"] diff = dict( before={}, after=payload @@ -214,14 +230,24 @@ def diff_mode_check(self, payload): def create_firmware_repository_profile(self): diff = {} - module_params = self.module_params() - payload = self.omevv_profile_obj.get_payload_details(**module_params) + payload = self.get_payload_details() res = FirmwareRepositoryProfile.execute(self) if res: diff = self.diff_mode_check(payload) - resp = self.omevv_profile_obj.create_firmware_repository_profile(payload) + resp, err_msg = self.omevv_profile_obj.create_firmware_repository_profile( + name=self.module.params.get('name'), + catalog_path=self.module.params.get('catalog_path'), + description=self.module.params.get('description'), + protocol_type=self.module.params.get('protocol_type'), + share_username=self.module.params.get('share_username'), + share_password=self.module.params.get('share_password'), + share_domain=self.module.params.get('share_domain') + ) if resp.success: profile_resp = self.omevv_profile_obj.get_firmware_repository_profile_by_id(resp.json_data) + while profile_resp.json_data["status"] != "Success": + time.sleep(1) + profile_resp = self.omevv_profile_obj.get_firmware_repository_profile_by_id(resp.json_data) if self.module._diff: self.module.exit_json(msg=SUCCESS_CREATION_MSG, profile_info=profile_resp.json_data, diff=diff, changed=True) self.module.exit_json(msg=SUCCESS_CREATION_MSG, profile_info=profile_resp.json_data, changed=True) @@ -229,8 +255,7 @@ def create_firmware_repository_profile(self): self.module.exit_json(msg=FAILED_CREATION_MSG, failed=True) def execute(self): - module_params = self.module_params() - payload = self.omevv_profile_obj.get_payload_details(**module_params) + payload = self.get_payload_details() result = self.get_firmware_repository_profile() profile = self.module.params.get('name') new_profile = self.module.params.get('new_name') @@ -262,8 +287,6 @@ def __init__(self, module, rest_obj): def diff_check(self, api_response, module_response): diff = {} - del module_response["protocolType"] - del module_response["shareCredential"] api_response["sharePath"] = api_response["sharePath"] + '/' + api_response["fileName"] for key in module_response.keys(): if key not in api_response or api_response[key] != module_response[key]: @@ -277,26 +300,38 @@ def trim_api_response(self, api_response): trimmed_resp["description"] = api_response["description"] return trimmed_resp - def diff_mode_check(self, payload, api_response): - diff = {} + def rec_diff(self, api_response, payload): + trim = self.trim_api_response(api_response) del payload["shareCredential"] - payload["profileName"] = self.module.params.get('new_name') + output = recursive_diff(trim, payload) if self.module._diff: diff = dict( - before=self.diff_check(api_response, payload), - after=payload + before=output[0], + after=output[1] ) return diff def modify_firmware_repository_profile(self, api_response): - module_params = self.module_params() - payload = self.omevv_profile_obj.get_modify_payload_details(**module_params) + payload = self.get_payload_details() + del payload["profileType"] + del payload["protocolType"] res = FirmwareRepositoryProfile.execute(self) if res: - diff = self.diff_mode_check(payload, self.trim_api_response(api_response)) - resp = self.omevv_profile_obj.modify_firmware_repository_profile(api_response["id"], payload) + diff = self.rec_diff(api_response, payload) + resp, err_msg = self.omevv_profile_obj.modify_firmware_repository_profile( + api_response["id"], + name=self.module.params.get('new_name'), + catalog_path=self.module.params.get('catalog_path'), + description=self.module.params.get('description'), + share_username=self.module.params.get('share_username'), + share_password=self.module.params.get('share_password'), + share_domain=self.module.params.get('share_domain') + ) if resp.success: profile_resp = self.omevv_profile_obj.get_firmware_repository_profile_by_id(api_response["id"]) + while profile_resp.json_data["status"] != "Success": + time.sleep(1) + profile_resp = self.omevv_profile_obj.get_firmware_repository_profile_by_id(api_response["id"]) if self.module._diff: self.module.exit_json(msg=SUCCESS_MODIFY_MSG, profile_info=profile_resp.json_data, diff=diff, changed=True) self.module.exit_json(msg=SUCCESS_MODIFY_MSG, profile_info=profile_resp.json_data, changed=True) @@ -304,16 +339,17 @@ def modify_firmware_repository_profile(self, api_response): self.module.exit_json(msg=FAILED_MODIFY_MSG, failed=True) def execute(self): - module_params = self.module_params() - payload = self.omevv_profile_obj.get_modify_payload_details() + payload = self.get_payload_details() + del payload["profileType"] + del payload["protocolType"] result = self.get_firmware_repository_profile() profile = self.module.params.get('name') api_response = self.omevv_profile_obj.search_profile_name(result, profile) - module_response = self.omevv_profile_obj.get_payload_details(**module_params) + module_response = self.get_payload_details() module_response["profileName"] = self.module.params.get('new_name') diff = self.diff_check(api_response, module_response) if diff and self.module.check_mode and self.module._diff: - diff = self.diff_mode_check(payload, self.trim_api_response(api_response)) + diff = self.rec_diff(api_response, payload) self.module.exit_json(msg=CHANGES_FOUND_MSG, diff=diff, changed=True) if diff and self.module.check_mode: self.module.exit_json(msg=CHANGES_FOUND_MSG, changed=True) @@ -367,9 +403,9 @@ def execute(self): if not profile_exists and self.module.check_mode: self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False) if not profile_exists and not self.module.check_mode and self.module._diff: - self.module.exit_json(msg=PROFILE_NOT_FOUND_MSG.format(profile_name=profile), diff={"before": {}, "after": {}}, profile_info={}, changed=False) + self.module.exit_json(msg=PROFILE_NOT_FOUND_MSG.format(profile_name=profile), diff={"before": {}, "after": {}}, profile_info={}, failed=True) if not profile_exists and not self.module.check_mode: - self.module.exit_json(msg=PROFILE_NOT_FOUND_MSG.format(profile_name=profile), profile_info={}, changed=False) + self.module.exit_json(msg=PROFILE_NOT_FOUND_MSG.format(profile_name=profile), profile_info={}, failed=True) if profile_exists and not self.module.check_mode: self.delete_firmware_repository_profile(api_response) if profile_exists and self.module.check_mode: From 50ed268edd2be1f9a5372cb40c271c667ca55cc0 Mon Sep 17 00:00:00 2001 From: Shivam Sharma Date: Fri, 18 Oct 2024 00:51:53 -0400 Subject: [PATCH 06/10] sanity fixed --- plugins/module_utils/omevv_firmware_utils.py | 38 ++++++++++--------- .../omevv_firmware_repository_profile.py | 19 +++++++++- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/plugins/module_utils/omevv_firmware_utils.py b/plugins/module_utils/omevv_firmware_utils.py index d5534bf91..621ada504 100644 --- a/plugins/module_utils/omevv_firmware_utils.py +++ b/plugins/module_utils/omevv_firmware_utils.py @@ -59,7 +59,7 @@ def get_create_payload_details(self, name, catalog_path, description, protocol_t "domain": share_domain } return payload - + def get_modify_payload_details(self, name, catalog_path, description, share_username, share_password, share_domain): payload = {} payload["profileName"] = name @@ -135,7 +135,8 @@ def test_connection(self, protocol_type, catalog_path, share_username, share_pas Tests the connection to the vCenter server. """ - payload = self.form_conn_payload(protocol_type, catalog_path, share_username, share_password, share_domain) + payload = self.form_conn_payload( + protocol_type, catalog_path, share_username, share_password, share_domain) resp = self.omevv.invoke_request("POST", TEST_CONNECTION_URI, payload) return resp @@ -152,26 +153,27 @@ def get_firmware_repository_profile_by_id(self, profile_id): Retrieves all firmware repository profile Information. """ - resp = self.omevv.invoke_request("GET", PROFILE_URI + "/" + str(profile_id)) + resp = self.omevv.invoke_request( + "GET", PROFILE_URI + "/" + str(profile_id)) return resp - def create_firmware_repository_profile(self, name, catalog_path, - description, protocol_type, + def create_firmware_repository_profile(self, name, catalog_path, + description, protocol_type, share_username, share_password, share_domain): err_msg = None if name is None: err_msg = "" - payload = self.get_create_payload_details(name, catalog_path, - description, protocol_type, - share_username, share_password, - share_domain) + payload = self.get_create_payload_details(name, catalog_path, + description, protocol_type, + share_username, share_password, + share_domain) resp = self.omevv.invoke_request("POST", PROFILE_URI, payload) return resp, err_msg - def modify_firmware_repository_profile(self, profile_id, name, catalog_path, - description, + def modify_firmware_repository_profile(self, profile_id, name, catalog_path, + description, share_username, share_password, share_domain): """ @@ -181,11 +183,12 @@ def modify_firmware_repository_profile(self, profile_id, name, catalog_path, err_msg = None if name is None: err_msg = "" - payload = self.get_modify_payload_details(name, catalog_path, - description, - share_username, share_password, - share_domain) - resp = self.omevv.invoke_request("PUT", PROFILE_URI + "/" + str(profile_id), payload) + payload = self.get_modify_payload_details(name, catalog_path, + description, + share_username, share_password, + share_domain) + resp = self.omevv.invoke_request( + "PUT", PROFILE_URI + "/" + str(profile_id), payload) return resp, err_msg def delete_firmware_repository_profile(self, profile_id): @@ -193,5 +196,6 @@ def delete_firmware_repository_profile(self, profile_id): Deletes a firmware repository profile. """ - resp = self.omevv.invoke_request("DELETE", PROFILE_URI + "/" + str(profile_id)) + resp = self.omevv.invoke_request( + "DELETE", PROFILE_URI + "/" + str(profile_id)) return resp diff --git a/plugins/modules/omevv_firmware_repository_profile.py b/plugins/modules/omevv_firmware_repository_profile.py index abb453a80..a2c59215b 100644 --- a/plugins/modules/omevv_firmware_repository_profile.py +++ b/plugins/modules/omevv_firmware_repository_profile.py @@ -201,6 +201,13 @@ def test_connection(self): else: self.module.exit_json(msg=FAILED_CONN_MSG, failed=True) + def trim_api_response(self, api_response): + trimmed_resp = {} + trimmed_resp["profileName"] = api_response["profileName"] + trimmed_resp["sharePath"] = api_response["sharePath"] + trimmed_resp["description"] = api_response["description"] + return trimmed_resp + def get_firmware_repository_profile(self): resp = self.omevv_profile_obj.get_firmware_repository_profile() data = resp.json_data @@ -260,6 +267,12 @@ def execute(self): profile = self.module.params.get('name') new_profile = self.module.params.get('new_name') profile_exists = self.omevv_profile_obj.search_profile_name(result, profile) + modified_payload = payload + del modified_payload["protocolType"] + del modified_payload["profileType"] + del modified_payload["shareCredential"] + trimmed_resp = FirmwareRepositoryProfile.trim_api_response(self, profile_exists) + diff = recursive_diff(modified_payload, trimmed_resp) if not profile_exists and self.module.check_mode and self.module._diff: diff = self.diff_mode_check(payload) self.module.exit_json(msg=CHANGES_FOUND_MSG, diff=diff, changed=True) @@ -271,7 +284,7 @@ def execute(self): self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False, diff={"before": {}, "after": {}}) if profile_exists and self.module.check_mode and not new_profile: self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False) - if profile_exists and new_profile: + if profile_exists and (new_profile or diff[0] != diff[1]): omevv_obj = ModifyFirmwareRepositoryProfile(self.module, self.obj) omevv_obj.execute() else: @@ -301,6 +314,7 @@ def trim_api_response(self, api_response): return trimmed_resp def rec_diff(self, api_response, payload): + diff = {} trim = self.trim_api_response(api_response) del payload["shareCredential"] output = recursive_diff(trim, payload) @@ -316,11 +330,12 @@ def modify_firmware_repository_profile(self, api_response): del payload["profileType"] del payload["protocolType"] res = FirmwareRepositoryProfile.execute(self) + name = self.module.params.get('new_name') if self.module.params.get('new_name') is not None else self.module.params.get('name') if res: diff = self.rec_diff(api_response, payload) resp, err_msg = self.omevv_profile_obj.modify_firmware_repository_profile( api_response["id"], - name=self.module.params.get('new_name'), + name=name, catalog_path=self.module.params.get('catalog_path'), description=self.module.params.get('description'), share_username=self.module.params.get('share_username'), From 5d52f645c31fb37fff4228ffafb3bb0318286d7a Mon Sep 17 00:00:00 2001 From: Shivam Sharma Date: Mon, 21 Oct 2024 00:19:21 -0400 Subject: [PATCH 07/10] refactored code and added playbook file --- .../omevv_firmware_repository_proile.yml | 37 ++ .../{ => omevv_utils}/omevv_firmware_utils.py | 155 +++-- .../omevv_firmware_repository_profile.py | 147 +++-- .../test_omevv_firmware_repository_profile.py | 537 ++++++++++++++++-- 4 files changed, 719 insertions(+), 157 deletions(-) create mode 100644 playbooks/omevv/omevv_firmware_repository_proile.yml rename plugins/module_utils/{ => omevv_utils}/omevv_firmware_utils.py (57%) diff --git a/playbooks/omevv/omevv_firmware_repository_proile.yml b/playbooks/omevv/omevv_firmware_repository_proile.yml new file mode 100644 index 000000000..610cdb099 --- /dev/null +++ b/playbooks/omevv/omevv_firmware_repository_proile.yml @@ -0,0 +1,37 @@ +--- +- name: Dell OpenManage Ansible device inventory details. + hosts: omevv + gather_facts: false + tasks: + - name: Create an OMEVV firmware repository profile + dellemc.openmanage.omevv_firmware_repository_profile: + hostname: "192.168.0.1" + vcenter_uuid: "xxxxx" + vcenter_username: "username" + vcenter_password: "password" + ca_path: "path/to/ca_file" + state: present + name: profile-1 + catalog_path: http://xx.xx.xx.xx/share/Catalog/Catalog.xml + + - name: Modify an OMEVV firmware repository profile + dellemc.openmanage.omevv_firmware_repository_profile: + hostname: "192.168.0.1" + vcenter_uuid: "xxxxx" + vcenter_username: "username" + vcenter_password: "password" + ca_path: "path/to/ca_file" + state: present + name: profile-1 + new_name: profile-2 + catalog_path: http://xx.xx.xx.xx/new_share/Catalog/Catalog.xml + + - name: Delete an OMEVV firmware repository profile + dellemc.openmanage.omevv_firmware_repository_profile: + hostname: "192.168.0.1" + vcenter_uuid: "xxxxx" + vcenter_username: "username" + vcenter_password: "password" + ca_path: "path/to/ca_file" + state: absent + name: profile-1 diff --git a/plugins/module_utils/omevv_firmware_utils.py b/plugins/module_utils/omevv_utils/omevv_firmware_utils.py similarity index 57% rename from plugins/module_utils/omevv_firmware_utils.py rename to plugins/module_utils/omevv_utils/omevv_firmware_utils.py index 621ada504..11199eae3 100644 --- a/plugins/module_utils/omevv_firmware_utils.py +++ b/plugins/module_utils/omevv_utils/omevv_firmware_utils.py @@ -30,11 +30,6 @@ __metaclass__ = type -XML_EXT = ".xml" -GZ_EXT = ".gz" -XML_GZ_EXT = ".xml.gz" -HTTP_PATH = "http" -HTTPS_PATH = "https://" PROFILE_URI = "/RepositoryProfiles" TEST_CONNECTION_URI = "/RepositoryProfiles/TestConnection" @@ -43,14 +38,45 @@ class OMEVVFirmwareProfile: def __init__(self, omevv): self.omevv = omevv + def get_firmware_repository_profile(self, profile_name=None): + """ + Retrieves the firmware repository profile information. + + Args: + profile_name (str, optional): The name of the profile to search for. Defaults to None. + + Returns: + list: The list of firmware repository profile information. + """ + resp = self.omevv.invoke_request('GET', PROFILE_URI) + profile_info = [] + if resp.success: + profile_info = resp.json_data + if profile_name: + profile_info = self.search_profile_name(profile_info, profile_name) + return profile_info + def get_create_payload_details(self, name, catalog_path, description, protocol_type, share_username, share_password, share_domain): + """ + Returns a dictionary containing the payload details for creating a firmware repository profile. + + Args: + name (str): The name of the firmware repository profile. + catalog_path (str): The path to the firmware catalog. + description (str, optional): The description of the firmware repository profile. + protocol_type (str): The protocol type of the firmware repository profile. + share_username (str): The username for the share credential. + share_password (str): The password for the share credential. + share_domain (str): The domain for the share credential. + + Returns: + dict: A dictionary containing the payload details for creating a firmware repository profile. + """ payload = {} payload["profileName"] = name payload["protocolType"] = protocol_type payload["sharePath"] = catalog_path - if description is None: - payload["description"] = "" - else: + if description is not None: payload["description"] = description payload["profileType"] = "Firmware" payload["shareCredential"] = { @@ -61,14 +87,25 @@ def get_create_payload_details(self, name, catalog_path, description, protocol_t return payload def get_modify_payload_details(self, name, catalog_path, description, share_username, share_password, share_domain): + """ + Returns a dictionary containing the payload details for modifying a firmware repository profile. + + Args: + name (str): The name of the firmware repository profile. + catalog_path (str): The path to the firmware catalog. + description (str, optional): The description of the firmware repository profile. + share_username (str): The username for the share credential. + share_password (str): The password for the share credential. + share_domain (str): The domain for the share credential. + + Returns: + dict: A dictionary containing the payload details for modifying a firmware repository profile. + """ payload = {} payload["profileName"] = name payload["sharePath"] = catalog_path - if description is None: - payload["description"] = "" - else: + if description is not None: payload["description"] = description - payload["profileType"] = "Firmware" payload["shareCredential"] = { "username": share_username, "password": share_password, @@ -77,6 +114,19 @@ def get_modify_payload_details(self, name, catalog_path, description, share_user return payload def form_conn_payload(self, protocol_type, catalog_path, share_username, share_password, share_domain): + """ + Returns a dictionary containing the payload details for testing the connection to a firmware repository. + + Args: + protocol_type (str): The protocol type of the firmware repository. + catalog_path (str): The path to the firmware catalog. + share_username (str): The username for the share credential. + share_password (str): The password for the share credential. + share_domain (str): The domain for the share credential. + + Returns: + dict: A dictionary containing the payload details for testing the connection to a firmware repository. + """ payload = {} payload["protocolType"] = protocol_type payload["catalogPath"] = catalog_path @@ -104,32 +154,6 @@ def search_profile_name(self, data, profile_name): return d return {} - def validate_catalog_path(self, protocol_type, catalog_path): - """ - Validates the catalog path based on the protocol type. - - Args: - protocol_type (str): The type of protocol used for the catalog path. - catalog_path (str): The path to the catalog. - - Raises: - SystemExit: If the catalog path is invalid. - - Returns: - None - """ - protocol_mapping = { - 'CIFS': (lambda path: path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT)), - 'NFS': (lambda path: path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT)), - 'HTTP': (lambda path: path.startswith(HTTP_PATH) and (path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT))), - "HTTPS": (lambda path: path.startswith(HTTPS_PATH) and (path.endswith(XML_EXT) or path.endswith(GZ_EXT) or path.endswith(XML_GZ_EXT))) - } - validator = protocol_mapping.get(protocol_type) - if validator is None: - self.module.exit_json(msg="Invalid catalog_path", failed=True) - if not validator(catalog_path): - self.module.exit_json(msg="Invalid catalog_path", failed=True) - def test_connection(self, protocol_type, catalog_path, share_username, share_password, share_domain): """ Tests the connection to the vCenter server. @@ -140,14 +164,6 @@ def test_connection(self, protocol_type, catalog_path, share_username, share_pas resp = self.omevv.invoke_request("POST", TEST_CONNECTION_URI, payload) return resp - def get_firmware_repository_profile(self): - """ - Retrieves all firmware repository profile Information. - - """ - resp = self.omevv.invoke_request("GET", PROFILE_URI) - return resp - def get_firmware_repository_profile_by_id(self, profile_id): """ Retrieves all firmware repository profile Information. @@ -161,9 +177,30 @@ def create_firmware_repository_profile(self, name, catalog_path, description, protocol_type, share_username, share_password, share_domain): + """ + Creates a firmware repository profile. + + Args: + name (str): The name of the firmware repository profile. + catalog_path (str): The path to the firmware catalog. + description (str, optional): The description of the firmware repository profile. + protocol_type (str): The protocol type of the firmware repository profile. + share_username (str): The username for the share credential. + share_password (str): The password for the share credential. + share_domain (str): The domain for the share credential. + + Returns: + tuple: A tuple containing the response and an error message. + + Raises: + None. + + """ err_msg = None - if name is None: - err_msg = "" + required_params = [name, catalog_path, protocol_type] + missing_params = [param for param in required_params if param is None] + if missing_params: + err_msg = "Required parameters such as: " + ", ".join(missing_params) payload = self.get_create_payload_details(name, catalog_path, description, protocol_type, @@ -179,10 +216,28 @@ def modify_firmware_repository_profile(self, profile_id, name, catalog_path, """ Modifies a firmware repository profile. + Args: + profile_id (int): The ID of the firmware repository profile. + name (str): The new name of the firmware repository profile. + catalog_path (str): The new path to the firmware catalog. + description (str, optional): The new description of the firmware repository profile. + share_username (str): The new username for the share credential. + share_password (str): The new password for the share credential. + share_domain (str): The new domain for the share credential. + + Returns: + tuple: A tuple containing the response and an error message. + + Raises: + None. + """ err_msg = None - if name is None: - err_msg = "" + required_params = [name, catalog_path] + missing_params = [param for param in required_params if param is None] + if missing_params: + err_msg = "Required parameters such as: " + ", ".join(missing_params) + payload = self.get_modify_payload_details(name, catalog_path, description, share_username, share_password, diff --git a/plugins/modules/omevv_firmware_repository_profile.py b/plugins/modules/omevv_firmware_repository_profile.py index a2c59215b..b5b0946ab 100644 --- a/plugins/modules/omevv_firmware_repository_profile.py +++ b/plugins/modules/omevv_firmware_repository_profile.py @@ -144,18 +144,18 @@ from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError from ansible.module_utils.urls import ConnectionError from ansible_collections.dellemc.openmanage.plugins.module_utils.omevv import RestOMEVV, OMEVVAnsibleModule -from ansible_collections.dellemc.openmanage.plugins.module_utils.omevv_firmware_utils import OMEVVFirmwareProfile +from ansible_collections.dellemc.openmanage.plugins.module_utils.omevv_utils.omevv_firmware_utils import OMEVVFirmwareProfile from ansible.module_utils.common.dict_transformations import recursive_diff ODATA_REGEX = "(.*?)@odata" ODATA = "@odata.id" +XML_EXT = ".xml" +GZ_EXT = ".gz" MESSAGE_EXTENDED_INFO = "@Message.ExtendedInfo" -TEST_CONNECTION_URI = "/RepositoryProfiles/TestConnection" -PROFILE_URI = "/RepositoryProfiles" SUCCESS_CREATION_MSG = "Successfully created the OMEVV firmware repository profile." FAILED_CREATION_MSG = "Unable to create the OMEVV firmware repository profile." SUCCESS_MODIFY_MSG = "Successfully modified the OMEVV firmware repository profile." -FAILED_MODIFY_MSG = "Unable to modify the OMEVV firmware repository profile as the details are same." +FAILED_MODIFY_MSG = "Unable to modify the OMEVV firmware repository profile" SUCCESS_DELETION_MSG = "Successfully deleted the OMEVV firmware repository profile." FAILED_DELETION_MSG = "Unable to delete the OMEVV firmware repository profile." PROFILE_NOT_FOUND_MSG = "Unable to delete the profile {profile_name} because the profile name is invalid. Enter a valid profile name and retry the operation." @@ -174,24 +174,22 @@ def __init__(self, module, rest_obj): def get_payload_details(self): payload = {} payload["profileName"] = self.module.params.get('name') - payload["protocolType"] = self.module.params.get('protocol_type').upper() + payload["protocolType"] = self.module.params.get('protocol_type') payload["sharePath"] = self.module.params.get('catalog_path') - if self.module.params.get('description') is None: - payload["description"] = "" - else: + if self.module.params.get('description') is not None: payload["description"] = self.module.params.get('description') payload["profileType"] = "Firmware" payload["shareCredential"] = { "username": self.module.params.get('share_username'), "password": self.module.params.get('share_password'), - "domain": self.module.params.get('share_dommain') + "domain": self.module.params.get('share_domain') } return payload - def test_connection(self): + def test_connection(self, protocol_type, catalog_path): resp = self.omevv_profile_obj.test_connection( - protocol_type=self.module.params.get('protocol_type'), - catalog_path=self.module.params.get('catalog_path'), + protocol_type=self.module.params.get('protocol_type') if protocol_type is None else protocol_type, + catalog_path=self.module.params.get('catalog_path') if catalog_path is None else catalog_path, share_username=self.module.params.get('share_username'), share_password=self.module.params.get('share_password'), share_domain=self.module.params.get('share_domain') @@ -203,20 +201,19 @@ def test_connection(self): def trim_api_response(self, api_response): trimmed_resp = {} + if api_response["description"] is not None: + trimmed_resp["description"] = api_response["description"] + if api_response["protocolType"] == "CIFS" and not (api_response["sharePath"].endswith(XML_EXT) or api_response["sharePath"].endswith(GZ_EXT)): + api_response["sharePath"] = api_response["sharePath"] + '\\' + api_response["fileName"] + if not (api_response["sharePath"].endswith(XML_EXT) or api_response["sharePath"].endswith(GZ_EXT)): + api_response["sharePath"] = api_response["sharePath"] + '/' + api_response["fileName"] trimmed_resp["profileName"] = api_response["profileName"] trimmed_resp["sharePath"] = api_response["sharePath"] - trimmed_resp["description"] = api_response["description"] return trimmed_resp - def get_firmware_repository_profile(self): - resp = self.omevv_profile_obj.get_firmware_repository_profile() - data = resp.json_data - return data - def execute(self): - self.omevv_profile_obj.validate_catalog_path(self.module.params.get('protocol_type'), self.module.params.get('catalog_path')) - result = self.test_connection() - return result + # To be overridden by the subclasses + pass class CreateFirmwareRepositoryProfile(FirmwareRepositoryProfile): @@ -228,7 +225,8 @@ def __init__(self, module, rest_obj): def diff_mode_check(self, payload): diff = {} - del payload["shareCredential"] + if "shareCredential" in payload: + payload.pop("shareCredential") diff = dict( before={}, after=payload @@ -238,10 +236,10 @@ def diff_mode_check(self, payload): def create_firmware_repository_profile(self): diff = {} payload = self.get_payload_details() - res = FirmwareRepositoryProfile.execute(self) + res = FirmwareRepositoryProfile.test_connection(self, None, None) if res: diff = self.diff_mode_check(payload) - resp, err_msg = self.omevv_profile_obj.create_firmware_repository_profile( + resp, _err_msg = self.omevv_profile_obj.create_firmware_repository_profile( name=self.module.params.get('name'), catalog_path=self.module.params.get('catalog_path'), description=self.module.params.get('description'), @@ -252,28 +250,34 @@ def create_firmware_repository_profile(self): ) if resp.success: profile_resp = self.omevv_profile_obj.get_firmware_repository_profile_by_id(resp.json_data) - while profile_resp.json_data["status"] != "Success": - time.sleep(1) - profile_resp = self.omevv_profile_obj.get_firmware_repository_profile_by_id(resp.json_data) - if self.module._diff: + while profile_resp.json_data["status"] != "Success" and profile_resp.json_data["status"] != "Failed": + time.sleep(3) + profile_resp = self.omevv_profile_obj.get_firmware_repository_profile(resp.json_data["profileName"]) + if self.module._diff and profile_resp.json_data["status"] == "Success": self.module.exit_json(msg=SUCCESS_CREATION_MSG, profile_info=profile_resp.json_data, diff=diff, changed=True) - self.module.exit_json(msg=SUCCESS_CREATION_MSG, profile_info=profile_resp.json_data, changed=True) + elif profile_resp.json_data["status"] == "Success": + self.module.exit_json(msg=SUCCESS_CREATION_MSG, profile_info=profile_resp.json_data, changed=True) + else: + self.module.exit_json(msg=FAILED_CREATION_MSG, profile_info=profile_resp.json_data, failed=True) else: self.module.exit_json(msg=FAILED_CREATION_MSG, failed=True) def execute(self): + modified_payload = {} payload = self.get_payload_details() - result = self.get_firmware_repository_profile() + result = self.omevv_profile_obj.get_firmware_repository_profile() profile = self.module.params.get('name') - new_profile = self.module.params.get('new_name') profile_exists = self.omevv_profile_obj.search_profile_name(result, profile) - modified_payload = payload + modified_payload.update(payload) del modified_payload["protocolType"] del modified_payload["profileType"] del modified_payload["shareCredential"] - trimmed_resp = FirmwareRepositoryProfile.trim_api_response(self, profile_exists) - diff = recursive_diff(modified_payload, trimmed_resp) + if profile_exists: + trimmed_resp = FirmwareRepositoryProfile.trim_api_response(self, profile_exists) + diff = recursive_diff(modified_payload, trimmed_resp) + new_profile = diff and (diff[0] != diff[1]) if not profile_exists and self.module.check_mode and self.module._diff: + FirmwareRepositoryProfile.test_connection(self, None, None) diff = self.diff_mode_check(payload) self.module.exit_json(msg=CHANGES_FOUND_MSG, diff=diff, changed=True) if not profile_exists and self.module.check_mode: @@ -284,7 +288,7 @@ def execute(self): self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False, diff={"before": {}, "after": {}}) if profile_exists and self.module.check_mode and not new_profile: self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False) - if profile_exists and (new_profile or diff[0] != diff[1]): + if profile_exists and new_profile: omevv_obj = ModifyFirmwareRepositoryProfile(self.module, self.obj) omevv_obj.execute() else: @@ -300,23 +304,30 @@ def __init__(self, module, rest_obj): def diff_check(self, api_response, module_response): diff = {} - api_response["sharePath"] = api_response["sharePath"] + '/' + api_response["fileName"] + if api_response["protocolType"] == "CIFS" and not (api_response["sharePath"].endswith(XML_EXT) or api_response["sharePath"].endswith(GZ_EXT)): + api_response["sharePath"] = api_response["sharePath"] + '\\' + api_response["fileName"] + if not (api_response["sharePath"].endswith(XML_EXT) or api_response["sharePath"].endswith(GZ_EXT)): + api_response["sharePath"] = api_response["sharePath"] + '/' + api_response["fileName"] + if module_response["sharePath"] is None: + module_response["sharePath"] = api_response["sharePath"] for key in module_response.keys(): if key not in api_response or api_response[key] != module_response[key]: diff[key] = module_response[key] return diff - def trim_api_response(self, api_response): + def trim_api_response(self, api_response, payload=None): trimmed_resp = {} trimmed_resp["profileName"] = api_response["profileName"] trimmed_resp["sharePath"] = api_response["sharePath"] - trimmed_resp["description"] = api_response["description"] + if payload.get("description") is not None: + trimmed_resp["description"] = api_response["description"] return trimmed_resp def rec_diff(self, api_response, payload): diff = {} - trim = self.trim_api_response(api_response) - del payload["shareCredential"] + trim = self.trim_api_response(api_response, payload) + if payload.get("shareCredential") is not None: + del payload["shareCredential"] output = recursive_diff(trim, payload) if self.module._diff: diff = dict( @@ -325,51 +336,62 @@ def rec_diff(self, api_response, payload): ) return diff - def modify_firmware_repository_profile(self, api_response): - payload = self.get_payload_details() - del payload["profileType"] - del payload["protocolType"] - res = FirmwareRepositoryProfile.execute(self) + def modify_firmware_repository_profile(self, api_response, module_response): + protocol_type = api_response["protocolType"] + catalog_path = api_response["sharePath"] + res = FirmwareRepositoryProfile.test_connection(self, protocol_type, catalog_path) name = self.module.params.get('new_name') if self.module.params.get('new_name') is not None else self.module.params.get('name') if res: - diff = self.rec_diff(api_response, payload) - resp, err_msg = self.omevv_profile_obj.modify_firmware_repository_profile( + diff = self.rec_diff(api_response, module_response) + resp, _err_msg = self.omevv_profile_obj.modify_firmware_repository_profile( api_response["id"], name=name, - catalog_path=self.module.params.get('catalog_path'), - description=self.module.params.get('description'), + catalog_path=self.module.params.get('catalog_path') if self.module.params.get('catalog_path') is not None else api_response["sharePath"], + description=self.module.params.get('description') if self.module.params.get('description') is not None else api_response["description"], share_username=self.module.params.get('share_username'), share_password=self.module.params.get('share_password'), share_domain=self.module.params.get('share_domain') ) if resp.success: - profile_resp = self.omevv_profile_obj.get_firmware_repository_profile_by_id(api_response["id"]) - while profile_resp.json_data["status"] != "Success": - time.sleep(1) - profile_resp = self.omevv_profile_obj.get_firmware_repository_profile_by_id(api_response["id"]) - if self.module._diff: - self.module.exit_json(msg=SUCCESS_MODIFY_MSG, profile_info=profile_resp.json_data, diff=diff, changed=True) - self.module.exit_json(msg=SUCCESS_MODIFY_MSG, profile_info=profile_resp.json_data, changed=True) + self.output_modify_response(api_response, diff) else: self.module.exit_json(msg=FAILED_MODIFY_MSG, failed=True) + def output_modify_response(self, api_response, diff): + profile_resp = self.omevv_profile_obj.get_firmware_repository_profile_by_id(api_response["id"]) + while profile_resp.json_data["status"] != "Success" and profile_resp.json_data["status"] != "Failed": + time.sleep(3) + profile_resp = self.omevv_profile_obj.get_firmware_repository_profile_by_id(api_response["id"]) + if self.module._diff and profile_resp.json_data["status"] == "Success": + self.module.exit_json(msg=SUCCESS_MODIFY_MSG, profile_info=profile_resp.json_data, diff=diff, changed=True) + elif profile_resp.json_data["status"] == "Success": + self.module.exit_json(msg=SUCCESS_MODIFY_MSG, profile_info=profile_resp.json_data, changed=True) + else: + self.module.exit_json(msg=FAILED_MODIFY_MSG, profile_info=profile_resp.json_data, failed=True) + def execute(self): + module_response = {} payload = self.get_payload_details() del payload["profileType"] del payload["protocolType"] - result = self.get_firmware_repository_profile() + result = self.omevv_profile_obj.get_firmware_repository_profile() profile = self.module.params.get('name') api_response = self.omevv_profile_obj.search_profile_name(result, profile) - module_response = self.get_payload_details() - module_response["profileName"] = self.module.params.get('new_name') + module_response.update(payload) + new_name = self.module.params.get('new_name') + profile_name = self.module.params.get('name') + module_response["profileName"] = ( + new_name if new_name is not None else profile_name + ) + del module_response["shareCredential"] diff = self.diff_check(api_response, module_response) if diff and self.module.check_mode and self.module._diff: - diff = self.rec_diff(api_response, payload) + diff = self.rec_diff(api_response, module_response) self.module.exit_json(msg=CHANGES_FOUND_MSG, diff=diff, changed=True) if diff and self.module.check_mode: self.module.exit_json(msg=CHANGES_FOUND_MSG, changed=True) if diff and not self.module.check_mode: - self.modify_firmware_repository_profile(api_response) + self.modify_firmware_repository_profile(api_response, module_response) else: self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False) @@ -408,7 +430,7 @@ def delete_firmware_repository_profile(self, api_response): self.module.exit_json(msg=FAILED_DELETION_MSG, failed=True) def execute(self): - result = self.get_firmware_repository_profile() + result = self.omevv_profile_obj.get_firmware_repository_profile() profile = self.module.params.get('name') api_response = self.omevv_profile_obj.search_profile_name(result, profile) profile_exists = self.omevv_profile_obj.search_profile_name(result, profile) @@ -442,7 +464,6 @@ def main(): module = OMEVVAnsibleModule( argument_spec=argument_spec, required_if=[ - ["state", 'present', ("name", "catalog_path", "protocol_type")], ["protocol_type", "NFS", ("catalog_path",)], ["protocol_type", "CIFS", ("catalog_path", "share_username", "share_password")], ["protocol_type", "HTTP", ("catalog_path",)], diff --git a/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py b/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py index 14b871154..457aecf8c 100644 --- a/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py +++ b/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py @@ -23,11 +23,13 @@ from ansible.module_utils._text import to_text MODULE_PATH = 'ansible_collections.dellemc.openmanage.plugins.modules.omevv_firmware_repository_profile.' -MODULE_UTILS_PATH = 'ansible_collections.dellemc.openmanage.plugins.module_utils.omevv_firmware_utils.' +MODULE_UTILS_PATH = 'ansible_collections.dellemc.openmanage.plugins.module_utils.omevv_utils.omevv_firmware_utils.' SUCCESS_MSG = "Successfully retrieved the firmware repository profile information." NO_PROFILE_MSG = "Unable to complete the operation because the '{profile_name}' is not a valid 'profile_name'." +FAILED_CONN_MSG = "Unable to complete the operation. Please check the connection details." FAILED_MSG = "Unable to fetch the firmware repository profile information." INVOKE_REQ_KEY = "RestOMEVV.invoke_request" +GET_PAYLOAD_DETAILS = "FirmwareRepositoryProfile.get_payload_details" GET_PROFILE_INFO_KEY = "OMEVVFirmwareProfile.get_firmware_repository_profile" PERFORM_OPERATION_KEY = "FirmwareRepositoryProfile.execute" HTTP_ERROR = "http error message" @@ -51,36 +53,92 @@ def omevv_connection_firmware_repository_profile(self, mocker, omevv_firmware_re omevv_conn_mock.return_value.__enter__.return_value = omevv_firmware_repository_profile_mock return omevv_conn_mock - def test_get_firmware_repository_profile(self, omevv_connection_firmware_repository_profile, omevv_default_args, mocker): + def test_execute(self, omevv_default_args, omevv_connection_firmware_repository_profile): obj = MagicMock() - sample_resp = [ - { - "id": 1000, - "profileName": "Dell Default Catalog", - "description": "Latest Firmware From Dell", - "profileType": "Firmware", - "sharePath": SHARE_PATH, - "fileName": "catalog.xml", - "status": "Success", - "factoryCreated": True, - "factoryType": "Default", - "catalogCreatedDate": "2024-08-27T01:58:10Z", - "catalogLastChecked": "2024-09-09T19:30:16Z", - "checkCertificate": "", - "protocolType": "HTTPS", - "createdBy": "OMEVV Default", - "modifiedBy": "", - "owner": "OMEVV" + omevv_obj = self.module.FirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, obj) + omevv_obj.execute() + + def test_get_payload_details(self, mocker, omevv_connection_firmware_repository_profile, omevv_default_args): + obj = MagicMock() + omevv_obj = self.module.FirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, obj) + _expected_output = { + "profileName": "test", + "protocolType": "HTTPS", + "sharePath": "https://downloads.dell.com/catalog/catalog.xml.gz", + "description": "Test6", + "profileType": "Firmware", + "shareCredential": { + "username": "", + "password": "", + "domain": "" } - ] - obj.json_data = sample_resp - mocker.patch(MODULE_PATH + PERFORM_OPERATION_KEY, return_value=True) - mocker.patch(MODULE_PATH + GET_PROFILE_INFO_KEY, return_value=obj) + } + obj.params.get.return_value = { + "state": "present", + "name": "test", + "description": "Test6", + "protocol_type": "HTTPS", + "catalog_path": "https://downloads.dell.com/catalog/catalog.xml.gz", + } + result = omevv_obj.get_payload_details() + assert result + + def test_connection(self, omevv_connection_firmware_repository_profile, omevv_default_args, mocker): + obj = MagicMock() + # Scenario 1: When test connection is successful + obj.success = True + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.test_connection', return_value=obj) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.FirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.test_connection(None, None) + assert result is True + + # Scenario 2: When test connection is not successful + obj.success = False + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.test_connection', return_value=obj) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.FirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.test_connection(None, None) + assert result is None + + def test_trim_api_response(self, omevv_connection_firmware_repository_profile, omevv_default_args): + # Scenario 1: Complete api_response + api_response = { + "id": 1000, + "profileName": "Dell Default Catalog", + "description": "Latest Firmware From Dell", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH + } + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.FirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.trim_api_response(api_response) + assert result + + # Scenario 2: api_response without description + api_response = { + "id": 1000, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": None + } f_module = self.get_module_mock( params=omevv_default_args) - obj = self.module.FirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) - result = obj.get_firmware_repository_profile() - assert result == sample_resp + obj = self.module.FirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.trim_api_response(api_response) + assert result class TestCreateFirmwareRepositoryProfile(FakeAnsibleModule): @@ -98,34 +156,422 @@ def omevv_connection_firmware_repository_profile(self, mocker, omevv_firmware_re omevv_conn_mock.return_value.__enter__.return_value = omevv_firmware_repository_profile_mock return omevv_conn_mock + def test_diff_mode_check(self, omevv_connection_firmware_repository_profile, omevv_default_args): + # Scenario 1: payload with shareCredential + payload = { + "profileName": "test", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Test6", + "profileType": "Firmware", + "shareCredential": { + "username": "", + "password": "", + "domain": "" + } + } + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.CreateFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.diff_mode_check(payload) + assert result + + # Scenario 2: payload without shareCredential + payload = { + "profileName": "test", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Test6", + "profileType": "Firmware" + } + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.CreateFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.diff_mode_check(payload) + assert result + def test_create_firmware_repository_profile(self, omevv_connection_firmware_repository_profile, omevv_default_args, mocker): obj = MagicMock() + obj2 = MagicMock() # Scenario 1: When creation is success obj.success = True payload = { "profileName": "test", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, "description": "Test6", + "profileType": "Firmware", + "shareCredential": { + "username": "", + "password": "", + "domain": "" + } + } + obj2.json_data = { + "id": 1000, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell", + "status": "Success" + } + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.test_connection', return_value=True) + mocker.patch( + MODULE_PATH + 'CreateFirmwareRepositoryProfile.diff_mode_check', return_value={}) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.create_firmware_repository_profile', return_value=(obj, "")) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.get_firmware_repository_profile_by_id', return_value=obj2) + f_module = self.get_module_mock(params=omevv_default_args) + obj = self.module.CreateFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.create_firmware_repository_profile() + assert result is None + + # Scenario 2: When creation is failed + obj.success = False + payload = { + "profileName": "test", "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Test6", "profileType": "Firmware", - "sharePath": SHARE_PATH} - mocker.patch(MODULE_UTILS_PATH + 'OMEVVFirmwareProfile.get_payload_details', return_value=payload) - mocker.patch(MODULE_PATH + PERFORM_OPERATION_KEY, return_value=True) - mocker.patch(MODULE_PATH + GET_PROFILE_INFO_KEY, return_value=obj) + "shareCredential": { + "username": "", + "password": "", + "domain": "" + } + } + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.test_connection', return_value=True) + mocker.patch( + MODULE_PATH + 'CreateFirmwareRepositoryProfile.diff_mode_check', return_value={}) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.create_firmware_repository_profile', return_value=(obj, "")) f_module = self.get_module_mock( params=omevv_default_args) - obj = self.module.CreateFirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) - obj.create_firmware_repository_profile() + obj = self.module.CreateFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.create_firmware_repository_profile() + assert result is None - # Scenario 2: When creation is failed - obj2 = MagicMock() - obj2.status_code = 400 - mocker.patch(MODULE_UTILS_PATH + 'OMEVVFirmwareProfile.get_payload_details', return_value=payload) - mocker.patch(MODULE_PATH + PERFORM_OPERATION_KEY, return_value=True) - mocker.patch(MODULE_PATH + GET_PROFILE_INFO_KEY, return_value=obj2) + def test_execute(self, omevv_connection_firmware_repository_profile, omevv_default_args, mocker): + # Scenario 1: When profile exists + # obj = MagicMock() + # payload = { + # "profileName": "test", + # "protocolType": "HTTPS", + # "sharePath": SHARE_PATH, + # "description": "Test6", + # "profileType": "Firmware", + # "shareCredential": { + # "username": "", + # "password": "", + # "domain": "" + # } + # } + # obj.json_data = { + # "id": 1000, + # "profileName": "Dell Default Catalog", + # "protocolType": "HTTPS", + # "sharePath": SHARE_PATH, + # "description": "Latest Firmware From Dell", + # "status": "Success" + # } + # mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + # mocker.patch(MODULE_UTILS_PATH + 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value=obj) + # mocker.patch(MODULE_UTILS_PATH + 'OMEVVFirmwareProfile.search_profile_name', return_value=obj) + # mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.trim_api_response', return_value=(obj, "")) + # mocker.patch(MODULE_PATH + 'CreateFirmwareRepositoryProfile.diff_mode_check', return_value={}) + # mocker.patch(MODULE_PATH + 'recursive_diff', return_value=(obj, "")) + # f_module = self.get_module_mock( + # params=omevv_default_args) + # obj = self.module.CreateFirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) + # result = obj.execute() + # assert result is None + + # Scenario 2: When profile does not exist + obj = MagicMock() + payload = { + "profileName": "test", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Test6", + "profileType": "Firmware", + "shareCredential": { + "username": "", + "password": "", + "domain": "" + } + } + obj.json_data = { + "id": 1000, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell", + "status": "Success" + } + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value={}) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.search_profile_name', return_value=obj) + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.trim_api_response', return_value=(obj, "")) + mocker.patch( + MODULE_PATH + 'CreateFirmwareRepositoryProfile.diff_mode_check', return_value={}) + mocker.patch(MODULE_PATH + 'recursive_diff', return_value=(obj, "")) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.CreateFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.execute() + assert result is None + + +class TestModifyFirmwareRepositoryProfile(FakeAnsibleModule): + module = omevv_firmware_repository_profile + + @pytest.fixture + def omevv_firmware_repository_profile_mock(self): + omevv_obj = MagicMock() + return omevv_obj + + @pytest.fixture + def omevv_connection_firmware_repository_profile(self, mocker, omevv_firmware_repository_profile_mock): + omevv_conn_mock = mocker.patch(MODULE_PATH + 'RestOMEVV', + return_value=omevv_firmware_repository_profile_mock) + omevv_conn_mock.return_value.__enter__.return_value = omevv_firmware_repository_profile_mock + return omevv_conn_mock + + def test_diff_check(self, mocker, omevv_default_args, omevv_connection_firmware_repository_profile): + module_response = { + "profileName": "test", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Test6", + "profileType": "Firmware", + "shareCredential": { + "username": "", + "password": "", + "domain": "" + } + } + api_response = { + "id": 1000, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell", + "status": "Success" + } + diff = {'description': 'Latest Firmware From Dell', 'id': 1000, + 'profileName': 'Dell Default Catalog', 'status': 'Success'} + mocker.patch( + MODULE_PATH + 'ModifyFirmwareRepositoryProfile.diff_check', return_value=diff) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.ModifyFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.diff_check(api_response, module_response) + assert result == diff + + def test_trim_api_response(self, mocker, omevv_default_args, omevv_connection_firmware_repository_profile): + api_response = { + "id": 1000, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell", + "status": "Success" + } + trimmed_resp = { + "profileName": "Dell Default Catalog", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell" + } + mocker.patch( + MODULE_PATH + 'ModifyFirmwareRepositoryProfile.trim_api_response', return_value=trimmed_resp) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.ModifyFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.trim_api_response(api_response) + assert result == trimmed_resp + + def test_modify_firmware_repository_profile(self, mocker, omevv_default_args, omevv_connection_firmware_repository_profile): + obj = MagicMock() + # Scenario 1: When modification is required + obj.success = True + payload = { + "profileName": "test", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Test6", + "profileType": "Firmware", + "shareCredential": { + "username": "", + "password": "", + "domain": "" + } + } + api_response = { + "id": 1996, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell", + "status": "Success" + } + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.test_connection', return_value=True) + mocker.patch(MODULE_PATH + 'ModifyFirmwareRepositoryProfile.rec_diff', + return_value={"profileName": "test"}) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.modify_firmware_repository_profile', return_value=(obj, "")) + mocker.patch( + MODULE_PATH + 'ModifyFirmwareRepositoryProfile.output_modify_response', return_value=None) + f_module = self.get_module_mock(params=omevv_default_args) + obj = self.module.ModifyFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.modify_firmware_repository_profile(api_response, payload) + assert result is None + + # Scenario 2: When test connection is not successful + obj.success = False + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.test_connection', return_value=False) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.ModifyFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.modify_firmware_repository_profile(payload, api_response) + assert result is None + + def test_output_modify_response(self, mocker, omevv_default_args, omevv_connection_firmware_repository_profile): + obj = MagicMock() + obj.success = True + obj.json_data = { + "id": 1000, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell", + "status": "Success" + } + api_response = { + "id": 1996, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell", + "status": "Success" + } + diff = {'profileName': 'Dell Default'} + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.get_firmware_repository_profile_by_id', return_value=obj) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.modify_firmware_repository_profile', return_value=(obj, "")) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.ModifyFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.output_modify_response(api_response, diff) + assert result is None + + +class TestDeleteFirmwareRepositoryProfile(FakeAnsibleModule): + module = omevv_firmware_repository_profile + + @pytest.fixture + def omevv_firmware_repository_profile_mock(self): + omevv_obj = MagicMock() + return omevv_obj + + @pytest.fixture + def omevv_connection_firmware_repository_profile(self, mocker, omevv_firmware_repository_profile_mock): + omevv_conn_mock = mocker.patch(MODULE_PATH + 'RestOMEVV', + return_value=omevv_firmware_repository_profile_mock) + omevv_conn_mock.return_value.__enter__.return_value = omevv_firmware_repository_profile_mock + return omevv_conn_mock + + def test_diff_mode_check(self, omevv_connection_firmware_repository_profile, omevv_default_args): + # Scenario 1: payload with shareCredential + payload = { + "profileName": "test", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Test6", + "profileType": "Firmware", + } + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.DeleteFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.diff_mode_check(payload) + assert result + + def test_delete_firmware_repository_profile(self, mocker, omevv_default_args, omevv_connection_firmware_repository_profile): + obj = MagicMock() + # Scenario 1: when delete is successful + obj.success = True + api_response = { + "id": 1996, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell", + "status": "Success" + } + mocker.patch( + MODULE_PATH + 'DeleteFirmwareRepositoryProfile.diff_mode_check', return_value={}) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.delete_firmware_repository_profile', return_value=obj) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.DeleteFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.delete_firmware_repository_profile(api_response) + assert result is None + + # Scenario 2: When delete is not successful + obj.success = False + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.delete_firmware_repository_profile', return_value=obj) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.DeleteFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.delete_firmware_repository_profile(api_response) + assert result is None + + def test_execute(self, mocker, omevv_default_args, omevv_connection_firmware_repository_profile): + obj = MagicMock() + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value={}) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.search_profile_name', return_value={}) + mocker.patch( + MODULE_PATH + 'DeleteFirmwareRepositoryProfile.diff_mode_check', return_value={}) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.delete_firmware_repository_profile', return_value=obj) f_module = self.get_module_mock( params=omevv_default_args) - obj2 = self.module.CreateFirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) - obj2.create_firmware_repository_profile() + obj = self.module.DeleteFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.execute() + assert result is None @pytest.mark.parametrize("exc_type", [URLError, HTTPError, SSLValidationError, ConnectionError, TypeError, ValueError]) @@ -133,7 +579,8 @@ def test_omevv_firmware_repository_profile_main_exception_handling_case(self, ex omevv_firmware_repository_profile_mock): omevv_firmware_repository_profile_mock.status_code = 400 omevv_firmware_repository_profile_mock.success = False - json_str = to_text(json.dumps({"errorCode": "501", "message": "Error"})) + json_str = to_text(json.dumps( + {"errorCode": "501", "message": "Error"})) omevv_default_args.update({'state': 'absent', 'name': 'test'}) if exc_type in [HTTPError, SSLValidationError]: mocker.patch(MODULE_PATH + PERFORM_OPERATION_KEY, @@ -152,7 +599,8 @@ def test_omevv_firmware_repository_profile_main_exception_handling_case(self, ex assert 'msg' in result # Scenario 1: When errorCode is 18001 - error_string = to_text(json.dumps({'errorCode': '18001', 'message': "Error"})) + error_string = to_text(json.dumps( + {'errorCode': '18001', 'message': "Error"})) if exc_type in [HTTPError]: mocker.patch(MODULE_PATH + PERFORM_OPERATION_KEY, side_effect=exc_type(HTTP_ERROR_URL, 400, @@ -163,7 +611,8 @@ def test_omevv_firmware_repository_profile_main_exception_handling_case(self, ex assert 'msg' in res_out # Scenario 2: When errorCode is 500 - error_string = to_text(json.dumps({'errorCode': '500', 'message': "Error"})) + error_string = to_text(json.dumps( + {'errorCode': '500', 'message': "Error"})) if exc_type in [HTTPError, SSLValidationError]: mocker.patch(MODULE_PATH + PERFORM_OPERATION_KEY, side_effect=exc_type(HTTP_ERROR_URL, 400, From 4f7ea4a899d2ff7952455bf2ccacad512a052096 Mon Sep 17 00:00:00 2001 From: Shivam Sharma Date: Mon, 21 Oct 2024 05:10:51 -0400 Subject: [PATCH 08/10] improved UT --- tests/unit/plugins/modules/conftest.py | 7 + .../test_omevv_firmware_repository_profile.py | 350 +++++++++++++++--- 2 files changed, 308 insertions(+), 49 deletions(-) diff --git a/tests/unit/plugins/modules/conftest.py b/tests/unit/plugins/modules/conftest.py index ff455d6d8..73557152d 100644 --- a/tests/unit/plugins/modules/conftest.py +++ b/tests/unit/plugins/modules/conftest.py @@ -61,6 +61,13 @@ def ome_default_args(): return default_args +@pytest.fixture +def omevv_default_args(): + default_args = {'hostname': 'XX.XX.XX.XX', 'vcenter_uuid': 'vcenter_uuid', 'vcenter_username': 'vcenter_username', + 'vcenter_password': 'vcenter_password', "ca_path": "/path/ca_bundle"} + return default_args + + @pytest.fixture def idrac_default_args(): default_args = {"idrac_ip": "idrac_ip", "idrac_user": "idrac_user", "idrac_password": "idrac_password", diff --git a/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py b/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py index 457aecf8c..5ec2212dd 100644 --- a/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py +++ b/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py @@ -60,27 +60,30 @@ def test_execute(self, omevv_default_args, omevv_connection_firmware_repository_ omevv_obj.execute() def test_get_payload_details(self, mocker, omevv_connection_firmware_repository_profile, omevv_default_args): + # Scenario 1: payload details with description obj = MagicMock() omevv_obj = self.module.FirmwareRepositoryProfile( omevv_connection_firmware_repository_profile, obj) - _expected_output = { - "profileName": "test", - "protocolType": "HTTPS", - "sharePath": "https://downloads.dell.com/catalog/catalog.xml.gz", + obj.params.get.return_value = { + "state": "present", + "name": "test", "description": "Test6", - "profileType": "Firmware", - "shareCredential": { - "username": "", - "password": "", - "domain": "" - } + "protocol_type": "HTTPS", + "catalog_path": SHARE_PATH } + result = omevv_obj.get_payload_details() + assert result + + # Scenario 2: payload details with description as None + obj = MagicMock() + omevv_obj = self.module.FirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, obj) obj.params.get.return_value = { "state": "present", "name": "test", - "description": "Test6", "protocol_type": "HTTPS", - "catalog_path": "https://downloads.dell.com/catalog/catalog.xml.gz", + "catalog_path": SHARE_PATH, + "description": None } result = omevv_obj.get_payload_details() assert result @@ -140,6 +143,22 @@ def test_trim_api_response(self, omevv_connection_firmware_repository_profile, o result = obj.trim_api_response(api_response) assert result + # Scenario 3: api_response without sharePath + api_response = { + "id": 1000, + "profileName": "Dell Default Catalog", + "protocolType": "CIFS", + "sharePath": "/catalog/", + "fileName": "catalog", + "description": None + } + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.FirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.trim_api_response(api_response) + assert result + class TestCreateFirmwareRepositoryProfile(FakeAnsibleModule): module = omevv_firmware_repository_profile @@ -195,6 +214,7 @@ def test_diff_mode_check(self, omevv_connection_firmware_repository_profile, ome def test_create_firmware_repository_profile(self, omevv_connection_firmware_repository_profile, omevv_default_args, mocker): obj = MagicMock() obj2 = MagicMock() + obj3 = MagicMock() # Scenario 1: When creation is success obj.success = True payload = { @@ -217,6 +237,14 @@ def test_create_firmware_repository_profile(self, omevv_connection_firmware_repo "description": "Latest Firmware From Dell", "status": "Success" } + obj3.json_data = { + "id": 1000, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell", + "status": "Parsing" + } mocker.patch( MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) mocker.patch( @@ -226,7 +254,11 @@ def test_create_firmware_repository_profile(self, omevv_connection_firmware_repo mocker.patch(MODULE_UTILS_PATH + 'OMEVVFirmwareProfile.create_firmware_repository_profile', return_value=(obj, "")) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.get_firmware_repository_profile_by_id', return_value=obj2) + 'OMEVVFirmwareProfile.get_firmware_repository_profile_by_id', return_value=obj3) + mocker.patch(MODULE_PATH + + 'time.sleep', return_value=None) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value=obj2) f_module = self.get_module_mock(params=omevv_default_args) obj = self.module.CreateFirmwareRepositoryProfile( omevv_connection_firmware_repository_profile, f_module) @@ -262,42 +294,44 @@ def test_create_firmware_repository_profile(self, omevv_connection_firmware_repo result = obj.create_firmware_repository_profile() assert result is None + # Senario 3: When creation is failed because api_response's status is failed + obj = MagicMock() + obj.json_data = { + "id": 1000, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell", + "status": "Failed" + } + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.test_connection', return_value=True) + mocker.patch( + MODULE_PATH + 'CreateFirmwareRepositoryProfile.diff_mode_check', return_value={}) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.create_firmware_repository_profile', return_value=(obj, "")) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.get_firmware_repository_profile_by_id', return_value=obj) + f_module = self.get_module_mock(params=omevv_default_args) + obj = self.module.CreateFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.create_firmware_repository_profile() + assert result is None + + # Scenario 4: When test connection is not successful + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.test_connection', return_value=False) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.CreateFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.create_firmware_repository_profile() + assert result is None + def test_execute(self, omevv_connection_firmware_repository_profile, omevv_default_args, mocker): # Scenario 1: When profile exists - # obj = MagicMock() - # payload = { - # "profileName": "test", - # "protocolType": "HTTPS", - # "sharePath": SHARE_PATH, - # "description": "Test6", - # "profileType": "Firmware", - # "shareCredential": { - # "username": "", - # "password": "", - # "domain": "" - # } - # } - # obj.json_data = { - # "id": 1000, - # "profileName": "Dell Default Catalog", - # "protocolType": "HTTPS", - # "sharePath": SHARE_PATH, - # "description": "Latest Firmware From Dell", - # "status": "Success" - # } - # mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) - # mocker.patch(MODULE_UTILS_PATH + 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value=obj) - # mocker.patch(MODULE_UTILS_PATH + 'OMEVVFirmwareProfile.search_profile_name', return_value=obj) - # mocker.patch(MODULE_PATH + 'FirmwareRepositoryProfile.trim_api_response', return_value=(obj, "")) - # mocker.patch(MODULE_PATH + 'CreateFirmwareRepositoryProfile.diff_mode_check', return_value={}) - # mocker.patch(MODULE_PATH + 'recursive_diff', return_value=(obj, "")) - # f_module = self.get_module_mock( - # params=omevv_default_args) - # obj = self.module.CreateFirmwareRepositoryProfile(omevv_connection_firmware_repository_profile, f_module) - # result = obj.execute() - # assert result is None - - # Scenario 2: When profile does not exist obj = MagicMock() payload = { "profileName": "test", @@ -322,7 +356,7 @@ def test_execute(self, omevv_connection_firmware_repository_profile, omevv_defau mocker.patch( MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value={}) + 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value=obj) mocker.patch(MODULE_UTILS_PATH + 'OMEVVFirmwareProfile.search_profile_name', return_value=obj) mocker.patch( @@ -337,6 +371,38 @@ def test_execute(self, omevv_connection_firmware_repository_profile, omevv_defau result = obj.execute() assert result is None + # Scenario 2: When profile does not exists and check_mode is true + obj = MagicMock() + payload = { + "profileName": "test", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Test6", + "profileType": "Firmware", + "shareCredential": { + "username": "", + "password": "", + "domain": "" + } + } + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value={}) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.search_profile_name', return_value={}) + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.trim_api_response', return_value=(obj, "")) + mocker.patch( + MODULE_PATH + 'CreateFirmwareRepositoryProfile.diff_mode_check', return_value={}) + mocker.patch(MODULE_PATH + 'recursive_diff', return_value=(obj, "")) + f_module = self.get_module_mock( + params=omevv_default_args, check_mode=True) + obj = self.module.CreateFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.execute() + assert result is None + class TestModifyFirmwareRepositoryProfile(FakeAnsibleModule): module = omevv_firmware_repository_profile @@ -354,6 +420,7 @@ def omevv_connection_firmware_repository_profile(self, mocker, omevv_firmware_re return omevv_conn_mock def test_diff_check(self, mocker, omevv_default_args, omevv_connection_firmware_repository_profile): + # Scenario 1: Default scenario module_response = { "profileName": "test", "protocolType": "HTTPS", @@ -385,7 +452,42 @@ def test_diff_check(self, mocker, omevv_default_args, omevv_connection_firmware_ result = obj.diff_check(api_response, module_response) assert result == diff + # Scenario 2: api_response with CIFS + api_response = { + "id": 1000, + "profileName": "Dell Default Catalog", + "protocolType": "CIFS", + "sharePath": "/catalog/", + "description": "Latest Firmware From Dell", + "status": "Success", + "fileName": "Catalog.xml" + } + mocker.patch( + MODULE_PATH + 'ModifyFirmwareRepositoryProfile.diff_check', return_value={}) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.ModifyFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.diff_check(api_response, module_response) + assert result == {} + + # Scenario 3: module_response without share_path + module_response = { + "profileName": "test", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Test6", + "profileType": "Firmware" + } + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.ModifyFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.diff_check(api_response, module_response) + assert result == {} + def test_trim_api_response(self, mocker, omevv_default_args, omevv_connection_firmware_repository_profile): + # Scenario 1: Complete api_response api_response = { "id": 1000, "profileName": "Dell Default Catalog", @@ -408,6 +510,68 @@ def test_trim_api_response(self, mocker, omevv_default_args, omevv_connection_fi result = obj.trim_api_response(api_response) assert result == trimmed_resp + # Scenario 2: payload without description + api_response = { + "id": 1000, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "status": "Success" + } + payload = { + "profileName": "test", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "profileType": "Firmware", + "shareCredential": { + "username": "", + "password": "", + "domain": "" + } + } + trimmed_resp = { + "profileName": "Dell Default Catalog", + "sharePath": SHARE_PATH + } + mocker.patch( + MODULE_PATH + 'ModifyFirmwareRepositoryProfile.trim_api_response', return_value=trimmed_resp) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.ModifyFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.trim_api_response(api_response, payload) + assert result == trimmed_resp + + def test_rec_diff(self, mocker, omevv_default_args, omevv_connection_firmware_repository_profile): + api_response = { + "id": 1000, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell", + "status": "Success" + } + payload = { + "profileName": "test", + "sharePath": SHARE_PATH, + "shareCredential": { + "username": "", + "password": "", + "domain": "" + } + } + diff = { + 'after': {'profileName': 'test', 'sharePath': SHARE_PATH}, + 'before': {}} + mocker.patch( + MODULE_PATH + 'ModifyFirmwareRepositoryProfile.trim_api_response', return_value={}) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.ModifyFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.rec_diff(api_response, payload) + assert result == diff + def test_modify_firmware_repository_profile(self, mocker, omevv_default_args, omevv_connection_firmware_repository_profile): obj = MagicMock() # Scenario 1: When modification is required @@ -448,7 +612,19 @@ def test_modify_firmware_repository_profile(self, mocker, omevv_default_args, om result = obj.modify_firmware_repository_profile(api_response, payload) assert result is None - # Scenario 2: When test connection is not successful + # Scenario 2: When modification is not successful + obj.success = False + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.modify_firmware_repository_profile', return_value=(obj, "")) + mocker.patch( + MODULE_PATH + 'ModifyFirmwareRepositoryProfile.output_modify_response', return_value=None) + f_module = self.get_module_mock(params=omevv_default_args) + obj = self.module.ModifyFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.modify_firmware_repository_profile(api_response, payload) + assert result is None + + # Scenario 3: When test connection is not successful obj.success = False mocker.patch( MODULE_PATH + 'FirmwareRepositoryProfile.test_connection', return_value=False) @@ -460,9 +636,19 @@ def test_modify_firmware_repository_profile(self, mocker, omevv_default_args, om assert result is None def test_output_modify_response(self, mocker, omevv_default_args, omevv_connection_firmware_repository_profile): + # Scenario 1: When modification is successful obj = MagicMock() + obj2 = MagicMock() obj.success = True obj.json_data = { + "id": 1000, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell", + "status": "Parsing" + } + obj2.json_data = { "id": 1000, "profileName": "Dell Default Catalog", "protocolType": "HTTPS", @@ -479,6 +665,29 @@ def test_output_modify_response(self, mocker, omevv_default_args, omevv_connecti "status": "Success" } diff = {'profileName': 'Dell Default'} + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.get_firmware_repository_profile_by_id', side_effect=[obj, obj2]) + mocker.patch(MODULE_PATH + + 'time.sleep', return_value={}) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.modify_firmware_repository_profile', return_value=(obj, "")) + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.ModifyFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.output_modify_response(api_response, diff) + assert result is None + + # Scenario 2: When modification is not successful + obj.json_data = { + "id": 1000, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell", + "status": "Failed" + } + diff = {'profileName': 'Dell Default'} mocker.patch(MODULE_UTILS_PATH + 'OMEVVFirmwareProfile.get_firmware_repository_profile_by_id', return_value=obj) mocker.patch(MODULE_UTILS_PATH + @@ -557,6 +766,7 @@ def test_delete_firmware_repository_profile(self, mocker, omevv_default_args, om assert result is None def test_execute(self, mocker, omevv_default_args, omevv_connection_firmware_repository_profile): + # Scenario 1: When profile does not exist obj = MagicMock() mocker.patch(MODULE_UTILS_PATH + 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value={}) @@ -573,6 +783,48 @@ def test_execute(self, mocker, omevv_default_args, omevv_connection_firmware_rep result = obj.execute() assert result is None + # Scenario 2: When profile does not exists and check_mode is true + f_module = self.get_module_mock( + params=omevv_default_args, check_mode=True) + obj = self.module.DeleteFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.execute() + assert result is None + + # Scenario 3: When profile exists and check_mode is true + obj = MagicMock() + obj.success = True + res = { + "id": 1996, + "profileName": "Dell Default Catalog", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Latest Firmware From Dell", + "status": "Success" + } + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value=res) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.search_profile_name', return_value=res) + mocker.patch( + MODULE_PATH + 'DeleteFirmwareRepositoryProfile.diff_mode_check', return_value={}) + mocker.patch(MODULE_UTILS_PATH + + 'OMEVVFirmwareProfile.delete_firmware_repository_profile', return_value=obj) + f_module = self.get_module_mock( + params=omevv_default_args, check_mode=True) + obj = self.module.DeleteFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.execute() + assert result is None + + # Scenario 4: When profile exists and check_mode is false + f_module = self.get_module_mock( + params=omevv_default_args) + obj = self.module.DeleteFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + result = obj.execute() + assert result is None + @pytest.mark.parametrize("exc_type", [URLError, HTTPError, SSLValidationError, ConnectionError, TypeError, ValueError]) def test_omevv_firmware_repository_profile_main_exception_handling_case(self, exc_type, mocker, omevv_default_args, From 7066e0de244cdaa1e59cd2734e1b6a8b659757b9 Mon Sep 17 00:00:00 2001 From: Shivam Sharma Date: Mon, 21 Oct 2024 07:04:48 -0400 Subject: [PATCH 09/10] fixed sonar issues --- .../test_omevv_firmware_repository_profile.py | 181 ++++++++++-------- 1 file changed, 96 insertions(+), 85 deletions(-) diff --git a/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py b/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py index 5ec2212dd..2588bc777 100644 --- a/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py +++ b/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py @@ -32,10 +32,21 @@ GET_PAYLOAD_DETAILS = "FirmwareRepositoryProfile.get_payload_details" GET_PROFILE_INFO_KEY = "OMEVVFirmwareProfile.get_firmware_repository_profile" PERFORM_OPERATION_KEY = "FirmwareRepositoryProfile.execute" +PERFORM_TEST_CONNECTION = "FirmwareRepositoryProfile.test_connection" +PERFORM_CREATE_PROFILE = "OMEVVFirmwareProfile.create_firmware_repository_profile" +PERFORM_MODIFY_PROFILE = "OMEVVFirmwareProfile.modify_firmware_repository_profile" +PERFORM_DELETE_PROFILE = "OMEVVFirmwareProfile.delete_firmware_repository_profile" +PERFORM_TRIM = "ModifyFirmwareRepositoryProfile.trim_api_response" +GET_PROFILE_BY_ID = "OMEVVFirmwareProfile.get_firmware_repository_profile_by_id" +SEARCH_PROFILE_NAME = "OMEVVFirmwareProfile.search_profile_name" +CREATE_DIFF_MODE_CHECK = "CreateFirmwareRepositoryProfile.diff_mode_check" +DELETE_DIFF_MODE_CHECK = "DeleteFirmwareRepositoryProfile.diff_mode_check" HTTP_ERROR = "http error message" HTTP_ERROR_URL = 'https://testhost.com' RETURN_TYPE = "application/json" SHARE_PATH = "https://downloads.dell.com//catalog/catalog.xml.gz" +PROFILE_NAME = "Dell Default Catalog" +DESCRIPTION = "Latest Firmware From Dell" class TestFirmwareRepositoryProfile(FakeAnsibleModule): @@ -116,8 +127,8 @@ def test_trim_api_response(self, omevv_connection_firmware_repository_profile, o # Scenario 1: Complete api_response api_response = { "id": 1000, - "profileName": "Dell Default Catalog", - "description": "Latest Firmware From Dell", + "profileName": PROFILE_NAME, + "description": DESCRIPTION, "protocolType": "HTTPS", "sharePath": SHARE_PATH } @@ -131,7 +142,7 @@ def test_trim_api_response(self, omevv_connection_firmware_repository_profile, o # Scenario 2: api_response without description api_response = { "id": 1000, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, "description": None @@ -146,7 +157,7 @@ def test_trim_api_response(self, omevv_connection_firmware_repository_profile, o # Scenario 3: api_response without sharePath api_response = { "id": 1000, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "CIFS", "sharePath": "/catalog/", "fileName": "catalog", @@ -231,34 +242,34 @@ def test_create_firmware_repository_profile(self, omevv_connection_firmware_repo } obj2.json_data = { "id": 1000, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Success" } obj3.json_data = { "id": 1000, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Parsing" } mocker.patch( - MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + MODULE_PATH + GET_PAYLOAD_DETAILS, return_value=payload) mocker.patch( - MODULE_PATH + 'FirmwareRepositoryProfile.test_connection', return_value=True) + MODULE_PATH + PERFORM_TEST_CONNECTION, return_value=True) mocker.patch( - MODULE_PATH + 'CreateFirmwareRepositoryProfile.diff_mode_check', return_value={}) + MODULE_PATH + CREATE_DIFF_MODE_CHECK, return_value={}) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.create_firmware_repository_profile', return_value=(obj, "")) + PERFORM_CREATE_PROFILE, return_value=(obj, "")) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.get_firmware_repository_profile_by_id', return_value=obj3) + GET_PROFILE_BY_ID, return_value=obj3) mocker.patch(MODULE_PATH + 'time.sleep', return_value=None) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value=obj2) + GET_PROFILE_INFO_KEY, return_value=obj2) f_module = self.get_module_mock(params=omevv_default_args) obj = self.module.CreateFirmwareRepositoryProfile( omevv_connection_firmware_repository_profile, f_module) @@ -280,13 +291,13 @@ def test_create_firmware_repository_profile(self, omevv_connection_firmware_repo } } mocker.patch( - MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + MODULE_PATH + GET_PAYLOAD_DETAILS, return_value=payload) mocker.patch( - MODULE_PATH + 'FirmwareRepositoryProfile.test_connection', return_value=True) + MODULE_PATH + PERFORM_TEST_CONNECTION, return_value=True) mocker.patch( - MODULE_PATH + 'CreateFirmwareRepositoryProfile.diff_mode_check', return_value={}) + MODULE_PATH + CREATE_DIFF_MODE_CHECK, return_value={}) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.create_firmware_repository_profile', return_value=(obj, "")) + PERFORM_CREATE_PROFILE, return_value=(obj, "")) f_module = self.get_module_mock( params=omevv_default_args) obj = self.module.CreateFirmwareRepositoryProfile( @@ -298,22 +309,22 @@ def test_create_firmware_repository_profile(self, omevv_connection_firmware_repo obj = MagicMock() obj.json_data = { "id": 1000, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Failed" } mocker.patch( - MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + MODULE_PATH + GET_PAYLOAD_DETAILS, return_value=payload) mocker.patch( - MODULE_PATH + 'FirmwareRepositoryProfile.test_connection', return_value=True) + MODULE_PATH + PERFORM_TEST_CONNECTION, return_value=True) mocker.patch( - MODULE_PATH + 'CreateFirmwareRepositoryProfile.diff_mode_check', return_value={}) + MODULE_PATH + CREATE_DIFF_MODE_CHECK, return_value={}) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.create_firmware_repository_profile', return_value=(obj, "")) + PERFORM_CREATE_PROFILE, return_value=(obj, "")) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.get_firmware_repository_profile_by_id', return_value=obj) + GET_PROFILE_BY_ID, return_value=obj) f_module = self.get_module_mock(params=omevv_default_args) obj = self.module.CreateFirmwareRepositoryProfile( omevv_connection_firmware_repository_profile, f_module) @@ -322,7 +333,7 @@ def test_create_firmware_repository_profile(self, omevv_connection_firmware_repo # Scenario 4: When test connection is not successful mocker.patch( - MODULE_PATH + 'FirmwareRepositoryProfile.test_connection', return_value=False) + MODULE_PATH + PERFORM_TEST_CONNECTION, return_value=False) f_module = self.get_module_mock( params=omevv_default_args) obj = self.module.CreateFirmwareRepositoryProfile( @@ -347,22 +358,22 @@ def test_execute(self, omevv_connection_firmware_repository_profile, omevv_defau } obj.json_data = { "id": 1000, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Success" } mocker.patch( - MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + MODULE_PATH + GET_PAYLOAD_DETAILS, return_value=payload) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value=obj) + GET_PROFILE_INFO_KEY, return_value=obj) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.search_profile_name', return_value=obj) + SEARCH_PROFILE_NAME, return_value=obj) mocker.patch( MODULE_PATH + 'FirmwareRepositoryProfile.trim_api_response', return_value=(obj, "")) mocker.patch( - MODULE_PATH + 'CreateFirmwareRepositoryProfile.diff_mode_check', return_value={}) + MODULE_PATH + CREATE_DIFF_MODE_CHECK, return_value={}) mocker.patch(MODULE_PATH + 'recursive_diff', return_value=(obj, "")) f_module = self.get_module_mock( params=omevv_default_args) @@ -386,15 +397,15 @@ def test_execute(self, omevv_connection_firmware_repository_profile, omevv_defau } } mocker.patch( - MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + MODULE_PATH + GET_PAYLOAD_DETAILS, return_value=payload) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value={}) + GET_PROFILE_INFO_KEY, return_value={}) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.search_profile_name', return_value={}) + SEARCH_PROFILE_NAME, return_value={}) mocker.patch( MODULE_PATH + 'FirmwareRepositoryProfile.trim_api_response', return_value=(obj, "")) mocker.patch( - MODULE_PATH + 'CreateFirmwareRepositoryProfile.diff_mode_check', return_value={}) + MODULE_PATH + CREATE_DIFF_MODE_CHECK, return_value={}) mocker.patch(MODULE_PATH + 'recursive_diff', return_value=(obj, "")) f_module = self.get_module_mock( params=omevv_default_args, check_mode=True) @@ -435,10 +446,10 @@ def test_diff_check(self, mocker, omevv_default_args, omevv_connection_firmware_ } api_response = { "id": 1000, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Success" } diff = {'description': 'Latest Firmware From Dell', 'id': 1000, @@ -455,10 +466,10 @@ def test_diff_check(self, mocker, omevv_default_args, omevv_connection_firmware_ # Scenario 2: api_response with CIFS api_response = { "id": 1000, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "CIFS", "sharePath": "/catalog/", - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Success", "fileName": "Catalog.xml" } @@ -490,19 +501,19 @@ def test_trim_api_response(self, mocker, omevv_default_args, omevv_connection_fi # Scenario 1: Complete api_response api_response = { "id": 1000, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Success" } trimmed_resp = { - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell" + "description": DESCRIPTION } mocker.patch( - MODULE_PATH + 'ModifyFirmwareRepositoryProfile.trim_api_response', return_value=trimmed_resp) + MODULE_PATH + PERFORM_TRIM, return_value=trimmed_resp) f_module = self.get_module_mock( params=omevv_default_args) obj = self.module.ModifyFirmwareRepositoryProfile( @@ -513,7 +524,7 @@ def test_trim_api_response(self, mocker, omevv_default_args, omevv_connection_fi # Scenario 2: payload without description api_response = { "id": 1000, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, "status": "Success" @@ -530,11 +541,11 @@ def test_trim_api_response(self, mocker, omevv_default_args, omevv_connection_fi } } trimmed_resp = { - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "sharePath": SHARE_PATH } mocker.patch( - MODULE_PATH + 'ModifyFirmwareRepositoryProfile.trim_api_response', return_value=trimmed_resp) + MODULE_PATH + PERFORM_TRIM, return_value=trimmed_resp) f_module = self.get_module_mock( params=omevv_default_args) obj = self.module.ModifyFirmwareRepositoryProfile( @@ -545,10 +556,10 @@ def test_trim_api_response(self, mocker, omevv_default_args, omevv_connection_fi def test_rec_diff(self, mocker, omevv_default_args, omevv_connection_firmware_repository_profile): api_response = { "id": 1000, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Success" } payload = { @@ -564,7 +575,7 @@ def test_rec_diff(self, mocker, omevv_default_args, omevv_connection_firmware_re 'after': {'profileName': 'test', 'sharePath': SHARE_PATH}, 'before': {}} mocker.patch( - MODULE_PATH + 'ModifyFirmwareRepositoryProfile.trim_api_response', return_value={}) + MODULE_PATH + PERFORM_TRIM, return_value={}) f_module = self.get_module_mock( params=omevv_default_args) obj = self.module.ModifyFirmwareRepositoryProfile( @@ -590,20 +601,20 @@ def test_modify_firmware_repository_profile(self, mocker, omevv_default_args, om } api_response = { "id": 1996, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Success" } mocker.patch( - MODULE_PATH + 'FirmwareRepositoryProfile.get_payload_details', return_value=payload) + MODULE_PATH + GET_PAYLOAD_DETAILS, return_value=payload) mocker.patch( - MODULE_PATH + 'FirmwareRepositoryProfile.test_connection', return_value=True) + MODULE_PATH + PERFORM_TEST_CONNECTION, return_value=True) mocker.patch(MODULE_PATH + 'ModifyFirmwareRepositoryProfile.rec_diff', return_value={"profileName": "test"}) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.modify_firmware_repository_profile', return_value=(obj, "")) + PERFORM_MODIFY_PROFILE, return_value=(obj, "")) mocker.patch( MODULE_PATH + 'ModifyFirmwareRepositoryProfile.output_modify_response', return_value=None) f_module = self.get_module_mock(params=omevv_default_args) @@ -615,7 +626,7 @@ def test_modify_firmware_repository_profile(self, mocker, omevv_default_args, om # Scenario 2: When modification is not successful obj.success = False mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.modify_firmware_repository_profile', return_value=(obj, "")) + PERFORM_MODIFY_PROFILE, return_value=(obj, "")) mocker.patch( MODULE_PATH + 'ModifyFirmwareRepositoryProfile.output_modify_response', return_value=None) f_module = self.get_module_mock(params=omevv_default_args) @@ -627,7 +638,7 @@ def test_modify_firmware_repository_profile(self, mocker, omevv_default_args, om # Scenario 3: When test connection is not successful obj.success = False mocker.patch( - MODULE_PATH + 'FirmwareRepositoryProfile.test_connection', return_value=False) + MODULE_PATH + PERFORM_TEST_CONNECTION, return_value=False) f_module = self.get_module_mock( params=omevv_default_args) obj = self.module.ModifyFirmwareRepositoryProfile( @@ -642,35 +653,35 @@ def test_output_modify_response(self, mocker, omevv_default_args, omevv_connecti obj.success = True obj.json_data = { "id": 1000, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Parsing" } obj2.json_data = { "id": 1000, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Success" } api_response = { "id": 1996, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Success" } diff = {'profileName': 'Dell Default'} mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.get_firmware_repository_profile_by_id', side_effect=[obj, obj2]) + GET_PROFILE_BY_ID, side_effect=[obj, obj2]) mocker.patch(MODULE_PATH + 'time.sleep', return_value={}) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.modify_firmware_repository_profile', return_value=(obj, "")) + PERFORM_MODIFY_PROFILE, return_value=(obj, "")) f_module = self.get_module_mock( params=omevv_default_args) obj = self.module.ModifyFirmwareRepositoryProfile( @@ -681,17 +692,17 @@ def test_output_modify_response(self, mocker, omevv_default_args, omevv_connecti # Scenario 2: When modification is not successful obj.json_data = { "id": 1000, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Failed" } diff = {'profileName': 'Dell Default'} mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.get_firmware_repository_profile_by_id', return_value=obj) + GET_PROFILE_BY_ID, return_value=obj) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.modify_firmware_repository_profile', return_value=(obj, "")) + PERFORM_MODIFY_PROFILE, return_value=(obj, "")) f_module = self.get_module_mock( params=omevv_default_args) obj = self.module.ModifyFirmwareRepositoryProfile( @@ -737,16 +748,16 @@ def test_delete_firmware_repository_profile(self, mocker, omevv_default_args, om obj.success = True api_response = { "id": 1996, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Success" } mocker.patch( - MODULE_PATH + 'DeleteFirmwareRepositoryProfile.diff_mode_check', return_value={}) + MODULE_PATH + DELETE_DIFF_MODE_CHECK, return_value={}) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.delete_firmware_repository_profile', return_value=obj) + PERFORM_DELETE_PROFILE, return_value=obj) f_module = self.get_module_mock( params=omevv_default_args) obj = self.module.DeleteFirmwareRepositoryProfile( @@ -757,7 +768,7 @@ def test_delete_firmware_repository_profile(self, mocker, omevv_default_args, om # Scenario 2: When delete is not successful obj.success = False mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.delete_firmware_repository_profile', return_value=obj) + PERFORM_DELETE_PROFILE, return_value=obj) f_module = self.get_module_mock( params=omevv_default_args) obj = self.module.DeleteFirmwareRepositoryProfile( @@ -769,13 +780,13 @@ def test_execute(self, mocker, omevv_default_args, omevv_connection_firmware_rep # Scenario 1: When profile does not exist obj = MagicMock() mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value={}) + GET_PROFILE_INFO_KEY, return_value={}) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.search_profile_name', return_value={}) + SEARCH_PROFILE_NAME, return_value={}) mocker.patch( - MODULE_PATH + 'DeleteFirmwareRepositoryProfile.diff_mode_check', return_value={}) + MODULE_PATH + DELETE_DIFF_MODE_CHECK, return_value={}) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.delete_firmware_repository_profile', return_value=obj) + PERFORM_DELETE_PROFILE, return_value=obj) f_module = self.get_module_mock( params=omevv_default_args) obj = self.module.DeleteFirmwareRepositoryProfile( @@ -796,20 +807,20 @@ def test_execute(self, mocker, omevv_default_args, omevv_connection_firmware_rep obj.success = True res = { "id": 1996, - "profileName": "Dell Default Catalog", + "profileName": PROFILE_NAME, "protocolType": "HTTPS", "sharePath": SHARE_PATH, - "description": "Latest Firmware From Dell", + "description": DESCRIPTION, "status": "Success" } mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.get_firmware_repository_profile', return_value=res) + GET_PROFILE_INFO_KEY, return_value=res) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.search_profile_name', return_value=res) + SEARCH_PROFILE_NAME, return_value=res) mocker.patch( - MODULE_PATH + 'DeleteFirmwareRepositoryProfile.diff_mode_check', return_value={}) + MODULE_PATH + DELETE_DIFF_MODE_CHECK, return_value={}) mocker.patch(MODULE_UTILS_PATH + - 'OMEVVFirmwareProfile.delete_firmware_repository_profile', return_value=obj) + PERFORM_DELETE_PROFILE, return_value=obj) f_module = self.get_module_mock( params=omevv_default_args, check_mode=True) obj = self.module.DeleteFirmwareRepositoryProfile( From 371c52d6b36ab2015062a99549af6a3d54b83bc6 Mon Sep 17 00:00:00 2001 From: Shivam Sharma Date: Tue, 22 Oct 2024 02:35:20 -0400 Subject: [PATCH 10/10] doc changes --- .../omevv_firmware_repository_profile.rst | 16 ++++++++-------- plugins/doc_fragments/omevv_auth_options.py | 12 ++++++------ .../modules/omevv_firmware_repository_profile.py | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/modules/omevv_firmware_repository_profile.rst b/docs/modules/omevv_firmware_repository_profile.rst index 70f5cfd60..c129a53eb 100644 --- a/docs/modules/omevv_firmware_repository_profile.rst +++ b/docs/modules/omevv_firmware_repository_profile.rst @@ -1,8 +1,8 @@ .. _omevv_firmware_repository_profile_module: -omevv_firmware_repository_profile -- Create, modify, and delete OMEVV firmware repository profile -================================================================================================= +omevv_firmware_repository_profile -- Create, modify, or delete OMEVV firmware repository profile +================================================================================================ .. contents:: :local: @@ -86,11 +86,11 @@ Parameters hostname (True, str, None) - OpenManage Enterprise or OpenManage Enterprise Modular IP address or hostname. + IP address or hostname of the OpenManage Enterprise Modular. vcenter_username (False, str, None) - OpenManage Enterprise Integration for VMware vCenter username. + Username for OpenManage Enterprise Integration for VMware vCenter (OMEVV). If the username is not provided, then the environment variable \ :envvar:`OMEVV\_VCENTER\_USERNAME`\ is used. @@ -98,7 +98,7 @@ Parameters vcenter_password (False, str, None) - OpenManage Enterprise Integration for VMware vCenter password. + Password for OpenManage Enterprise Integration for VMware vCenter (OMEVV). If the password is not provided, then the environment variable \ :envvar:`OMEVV\_VCENTER\_PASSWORD`\ is used. @@ -106,11 +106,11 @@ Parameters vcenter_uuid (False, str, None) - Universally unique identifier (uuid) of vCenter. + Universally Unique Identifier (UUID) of vCenter. - vCenter uuid details can be fetched using \ :ref:`dellemc.openmanage.omevv\_vcenter\_info `\ module. + vCenter UUID details can be retrieved using \ :ref:`dellemc.openmanage.omevv\_vcenter\_info `\ module. - If the uuid is not provided, then the environment variable \ :envvar:`OMEVV\_VCENTER\_UUID`\ is used. + If UUID is not provided, then the environment variable \ :envvar:`OMEVV\_VCENTER\_UUID`\ is used. Example: export OMEVV\_VCENTER\_UUID=uuid diff --git a/plugins/doc_fragments/omevv_auth_options.py b/plugins/doc_fragments/omevv_auth_options.py index 96ea1649b..c792f0ab9 100644 --- a/plugins/doc_fragments/omevv_auth_options.py +++ b/plugins/doc_fragments/omevv_auth_options.py @@ -18,28 +18,28 @@ class ModuleDocFragment(object): DOCUMENTATION = r''' options: hostname: - description: OpenManage Enterprise or OpenManage Enterprise Modular IP address or hostname. + description: IP address or hostname of the OpenManage Enterprise Modular. type: str required: true vcenter_username: description: - - OpenManage Enterprise Integration for VMware vCenter username. + - Username for OpenManage Enterprise Integration for VMware vCenter (OMEVV). - If the username is not provided, then the environment variable E(OMEVV_VCENTER_USERNAME) is used. - "Example: export OMEVV_VCENTER_USERNAME=username" type: str required: false vcenter_password: description: - - OpenManage Enterprise Integration for VMware vCenter password. + - Password for OpenManage Enterprise Integration for VMware vCenter (OMEVV). - If the password is not provided, then the environment variable E(OMEVV_VCENTER_PASSWORD) is used. - "Example: export OMEVV_VCENTER_PASSWORD=password" type: str required: false vcenter_uuid: description: - - Universally unique identifier (uuid) of vCenter. - - vCenter uuid details can be fetched using M(dellemc.openmanage.omevv_vcenter_info) module. - - If the uuid is not provided, then the environment variable E(OMEVV_VCENTER_UUID) is used. + - Universally Unique Identifier (UUID) of vCenter. + - vCenter UUID details can be retrieved using M(dellemc.openmanage.omevv_vcenter_info) module. + - If UUID is not provided, then the environment variable E(OMEVV_VCENTER_UUID) is used. - "Example: export OMEVV_VCENTER_UUID=uuid" type: str required: false diff --git a/plugins/modules/omevv_firmware_repository_profile.py b/plugins/modules/omevv_firmware_repository_profile.py index b5b0946ab..d070d1afa 100644 --- a/plugins/modules/omevv_firmware_repository_profile.py +++ b/plugins/modules/omevv_firmware_repository_profile.py @@ -17,7 +17,7 @@ DOCUMENTATION = r""" --- module: omevv_firmware_repository_profile -short_description: Create, modify, and delete OMEVV firmware repository profile +short_description: Create, modify, or delete OMEVV firmware repository profile version_added: "9.8.0" description: This module allows you to create, modify, or delete an OpenManage Enterprise Integration for VMware Center (OMEVV) firmware repository profile. extends_documentation_fragment: