diff --git a/docs/modules/omevv_firmware_repository_profile.rst b/docs/modules/omevv_firmware_repository_profile.rst new file mode 100644 index 000000000..c129a53eb --- /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, or 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) + IP address or hostname of the OpenManage Enterprise Modular. + + + vcenter_username (False, str, None) + 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. + + Example: export OMEVV\_VCENTER\_USERNAME=username + + + vcenter_password (False, str, None) + 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. + + Example: export OMEVV\_VCENTER\_PASSWORD=password + + + vcenter_uuid (False, str, None) + Universally Unique Identifier (UUID) of vCenter. + + vCenter UUID details can be retrieved using \ :ref:`dellemc.openmanage.omevv\_vcenter\_info `\ module. + + If 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/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/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 new file mode 100644 index 000000000..d070d1afa --- /dev/null +++ b/plugins/modules/omevv_firmware_repository_profile.py @@ -0,0 +1,500 @@ +#!/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, 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: + - dellemc.openmanage.omevv_auth_options +options: + state: + description: + - 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 + choices: [present, absent] + default: present + name: + description: + - 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. + type: str + new_name: + description: Name of the new OMEVV profile name when modify operation is performed. + type: str + protocol_type: + description: + - 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: + 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 parameter is required when I(catalog_path) is HTTPS or CIFS. + type: str + share_password: + description: + - Password of the share. + - This parameter 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 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. +""" + +EXAMPLES = r""" +--- +- 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 = 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 +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_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" +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" +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." + + +class FirmwareRepositoryProfile: + + def __init__(self, module, rest_obj): + self.module = module + self.obj = rest_obj + self.omevv_profile_obj = OMEVVFirmwareProfile(self.obj) + + def get_payload_details(self): + payload = {} + payload["profileName"] = self.module.params.get('name') + payload["protocolType"] = self.module.params.get('protocol_type') + payload["sharePath"] = self.module.params.get('catalog_path') + 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_domain') + } + return payload + + def test_connection(self, protocol_type, catalog_path): + resp = self.omevv_profile_obj.test_connection( + 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') + ) + if resp.success: + return True + else: + self.module.exit_json(msg=FAILED_CONN_MSG, failed=True) + + 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"] + return trimmed_resp + + def execute(self): + # To be overridden by the subclasses + pass + + +class CreateFirmwareRepositoryProfile(FirmwareRepositoryProfile): + + def __init__(self, module, rest_obj): + self.module = module + self.obj = rest_obj + super().__init__(module, rest_obj) + + def diff_mode_check(self, payload): + diff = {} + if "shareCredential" in payload: + payload.pop("shareCredential") + diff = dict( + before={}, + after=payload + ) + return diff + + def create_firmware_repository_profile(self): + diff = {} + payload = self.get_payload_details() + 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( + 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" 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) + 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.omevv_profile_obj.get_firmware_repository_profile() + profile = self.module.params.get('name') + profile_exists = self.omevv_profile_obj.search_profile_name(result, profile) + modified_payload.update(payload) + del modified_payload["protocolType"] + del modified_payload["profileType"] + del modified_payload["shareCredential"] + 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: + self.module.exit_json(msg=CHANGES_FOUND_MSG, changed=True) + if not profile_exists and not self.module.check_mode: + 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: + 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=CHANGES_NOT_FOUND_MSG, changed=False) + + +class ModifyFirmwareRepositoryProfile(FirmwareRepositoryProfile): + + def __init__(self, module, rest_obj): + self.module = module + self.obj = rest_obj + super().__init__(module, rest_obj) + + def diff_check(self, api_response, module_response): + diff = {} + 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, payload=None): + trimmed_resp = {} + trimmed_resp["profileName"] = api_response["profileName"] + trimmed_resp["sharePath"] = api_response["sharePath"] + 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, payload) + if payload.get("shareCredential") is not None: + del payload["shareCredential"] + output = recursive_diff(trim, payload) + if self.module._diff: + diff = dict( + before=output[0], + after=output[1] + ) + return diff + + 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, 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') 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: + 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.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.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, 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, module_response) + else: + self.module.exit_json(msg=CHANGES_NOT_FOUND_MSG, changed=False) + + +class DeleteFirmwareRepositoryProfile(FirmwareRepositoryProfile): + + def __init__(self, module, rest_obj): + self.module = module + self.obj = rest_obj + super().__init__(module, rest_obj) + + 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 = {} + 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: + 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.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) + 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={}, 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={}, 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: + self.module.exit_json(msg=CHANGES_FOUND_MSG, changed=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=[ + ["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) + try: + with RestOMEVV(module.params) as 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: + 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') + 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) + except URLError as err: + module.exit_json(msg=str(err), unreachable=True) + except (IOError, ValueError, TypeError, ConnectionError, + AttributeError, IndexError, KeyError, OSError) as err: + module.exit_json(msg=str(err), failed=True) + + +if __name__ == '__main__': + main() 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..2588bc777 --- /dev/null +++ b/tests/unit/plugins/modules/test_omevv_firmware_repository_profile.py @@ -0,0 +1,886 @@ +# -*- 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.' +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" +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): + 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_execute(self, omevv_default_args, omevv_connection_firmware_repository_profile): + obj = MagicMock() + 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): + # Scenario 1: payload details with description + 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": 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", + "protocol_type": "HTTPS", + "catalog_path": SHARE_PATH, + "description": None + } + 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": PROFILE_NAME, + "description": DESCRIPTION, + "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": PROFILE_NAME, + "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.trim_api_response(api_response) + assert result + + # Scenario 3: api_response without sharePath + api_response = { + "id": 1000, + "profileName": PROFILE_NAME, + "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 + + @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", + "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() + obj3 = 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": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": DESCRIPTION, + "status": "Success" + } + obj3.json_data = { + "id": 1000, + "profileName": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": DESCRIPTION, + "status": "Parsing" + } + mocker.patch( + MODULE_PATH + GET_PAYLOAD_DETAILS, return_value=payload) + mocker.patch( + MODULE_PATH + PERFORM_TEST_CONNECTION, return_value=True) + mocker.patch( + MODULE_PATH + CREATE_DIFF_MODE_CHECK, return_value={}) + mocker.patch(MODULE_UTILS_PATH + + PERFORM_CREATE_PROFILE, return_value=(obj, "")) + mocker.patch(MODULE_UTILS_PATH + + GET_PROFILE_BY_ID, return_value=obj3) + mocker.patch(MODULE_PATH + + 'time.sleep', return_value=None) + mocker.patch(MODULE_UTILS_PATH + + 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) + 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", + "shareCredential": { + "username": "", + "password": "", + "domain": "" + } + } + mocker.patch( + MODULE_PATH + GET_PAYLOAD_DETAILS, return_value=payload) + mocker.patch( + MODULE_PATH + PERFORM_TEST_CONNECTION, return_value=True) + mocker.patch( + MODULE_PATH + CREATE_DIFF_MODE_CHECK, return_value={}) + mocker.patch(MODULE_UTILS_PATH + + PERFORM_CREATE_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) + 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": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": DESCRIPTION, + "status": "Failed" + } + mocker.patch( + MODULE_PATH + GET_PAYLOAD_DETAILS, return_value=payload) + mocker.patch( + MODULE_PATH + PERFORM_TEST_CONNECTION, return_value=True) + mocker.patch( + MODULE_PATH + CREATE_DIFF_MODE_CHECK, return_value={}) + mocker.patch(MODULE_UTILS_PATH + + PERFORM_CREATE_PROFILE, return_value=(obj, "")) + mocker.patch(MODULE_UTILS_PATH + + 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) + result = obj.create_firmware_repository_profile() + assert result is None + + # Scenario 4: When test connection is not successful + mocker.patch( + MODULE_PATH + PERFORM_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": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": DESCRIPTION, + "status": "Success" + } + mocker.patch( + MODULE_PATH + GET_PAYLOAD_DETAILS, return_value=payload) + mocker.patch(MODULE_UTILS_PATH + + GET_PROFILE_INFO_KEY, return_value=obj) + mocker.patch(MODULE_UTILS_PATH + + SEARCH_PROFILE_NAME, return_value=obj) + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.trim_api_response', return_value=(obj, "")) + mocker.patch( + 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) + obj = self.module.CreateFirmwareRepositoryProfile( + omevv_connection_firmware_repository_profile, f_module) + 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 + GET_PAYLOAD_DETAILS, return_value=payload) + mocker.patch(MODULE_UTILS_PATH + + GET_PROFILE_INFO_KEY, return_value={}) + mocker.patch(MODULE_UTILS_PATH + + SEARCH_PROFILE_NAME, return_value={}) + mocker.patch( + MODULE_PATH + 'FirmwareRepositoryProfile.trim_api_response', return_value=(obj, "")) + mocker.patch( + 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) + 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): + # Scenario 1: Default scenario + module_response = { + "profileName": "test", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Test6", + "profileType": "Firmware", + "shareCredential": { + "username": "", + "password": "", + "domain": "" + } + } + api_response = { + "id": 1000, + "profileName": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": DESCRIPTION, + "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 + + # Scenario 2: api_response with CIFS + api_response = { + "id": 1000, + "profileName": PROFILE_NAME, + "protocolType": "CIFS", + "sharePath": "/catalog/", + "description": DESCRIPTION, + "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": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": DESCRIPTION, + "status": "Success" + } + trimmed_resp = { + "profileName": PROFILE_NAME, + "sharePath": SHARE_PATH, + "description": DESCRIPTION + } + mocker.patch( + MODULE_PATH + PERFORM_TRIM, 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 + + # Scenario 2: payload without description + api_response = { + "id": 1000, + "profileName": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "status": "Success" + } + payload = { + "profileName": "test", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "profileType": "Firmware", + "shareCredential": { + "username": "", + "password": "", + "domain": "" + } + } + trimmed_resp = { + "profileName": PROFILE_NAME, + "sharePath": SHARE_PATH + } + mocker.patch( + MODULE_PATH + PERFORM_TRIM, 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": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": DESCRIPTION, + "status": "Success" + } + payload = { + "profileName": "test", + "sharePath": SHARE_PATH, + "shareCredential": { + "username": "", + "password": "", + "domain": "" + } + } + diff = { + 'after': {'profileName': 'test', 'sharePath': SHARE_PATH}, + 'before': {}} + mocker.patch( + MODULE_PATH + PERFORM_TRIM, 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 + obj.success = True + payload = { + "profileName": "test", + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": "Test6", + "profileType": "Firmware", + "shareCredential": { + "username": "", + "password": "", + "domain": "" + } + } + api_response = { + "id": 1996, + "profileName": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": DESCRIPTION, + "status": "Success" + } + mocker.patch( + MODULE_PATH + GET_PAYLOAD_DETAILS, return_value=payload) + mocker.patch( + MODULE_PATH + PERFORM_TEST_CONNECTION, return_value=True) + mocker.patch(MODULE_PATH + 'ModifyFirmwareRepositoryProfile.rec_diff', + return_value={"profileName": "test"}) + mocker.patch(MODULE_UTILS_PATH + + 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) + 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 modification is not successful + obj.success = False + mocker.patch(MODULE_UTILS_PATH + + 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) + 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 + PERFORM_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): + # Scenario 1: When modification is successful + obj = MagicMock() + obj2 = MagicMock() + obj.success = True + obj.json_data = { + "id": 1000, + "profileName": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": DESCRIPTION, + "status": "Parsing" + } + obj2.json_data = { + "id": 1000, + "profileName": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": DESCRIPTION, + "status": "Success" + } + api_response = { + "id": 1996, + "profileName": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": DESCRIPTION, + "status": "Success" + } + diff = {'profileName': 'Dell Default'} + mocker.patch(MODULE_UTILS_PATH + + GET_PROFILE_BY_ID, side_effect=[obj, obj2]) + mocker.patch(MODULE_PATH + + 'time.sleep', return_value={}) + mocker.patch(MODULE_UTILS_PATH + + PERFORM_MODIFY_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": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": DESCRIPTION, + "status": "Failed" + } + diff = {'profileName': 'Dell Default'} + mocker.patch(MODULE_UTILS_PATH + + GET_PROFILE_BY_ID, return_value=obj) + mocker.patch(MODULE_UTILS_PATH + + PERFORM_MODIFY_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": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": DESCRIPTION, + "status": "Success" + } + mocker.patch( + MODULE_PATH + DELETE_DIFF_MODE_CHECK, return_value={}) + mocker.patch(MODULE_UTILS_PATH + + PERFORM_DELETE_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 + + PERFORM_DELETE_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): + # Scenario 1: When profile does not exist + obj = MagicMock() + mocker.patch(MODULE_UTILS_PATH + + GET_PROFILE_INFO_KEY, return_value={}) + mocker.patch(MODULE_UTILS_PATH + + SEARCH_PROFILE_NAME, return_value={}) + mocker.patch( + MODULE_PATH + DELETE_DIFF_MODE_CHECK, return_value={}) + mocker.patch(MODULE_UTILS_PATH + + PERFORM_DELETE_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.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": PROFILE_NAME, + "protocolType": "HTTPS", + "sharePath": SHARE_PATH, + "description": DESCRIPTION, + "status": "Success" + } + mocker.patch(MODULE_UTILS_PATH + + GET_PROFILE_INFO_KEY, return_value=res) + mocker.patch(MODULE_UTILS_PATH + + SEARCH_PROFILE_NAME, return_value=res) + mocker.patch( + MODULE_PATH + DELETE_DIFF_MODE_CHECK, return_value={}) + mocker.patch(MODULE_UTILS_PATH + + PERFORM_DELETE_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, + 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['changed'] is False + 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