diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-18 05:52:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-18 05:52:35 +0000 |
commit | 7fec0b69a082aaeec72fee0612766aa42f6b1b4d (patch) | |
tree | efb569b86ca4da888717f5433e757145fa322e08 /ansible_collections/dellemc/openmanage/plugins/module_utils | |
parent | Releasing progress-linux version 7.7.0+dfsg-3~progress7.99u1. (diff) | |
download | ansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.tar.xz ansible-7fec0b69a082aaeec72fee0612766aa42f6b1b4d.zip |
Merging upstream version 9.4.0+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/dellemc/openmanage/plugins/module_utils')
5 files changed, 280 insertions, 35 deletions
diff --git a/ansible_collections/dellemc/openmanage/plugins/module_utils/dellemc_idrac.py b/ansible_collections/dellemc/openmanage/plugins/module_utils/dellemc_idrac.py index fee5339c5..b2b2240d0 100644 --- a/ansible_collections/dellemc/openmanage/plugins/module_utils/dellemc_idrac.py +++ b/ansible_collections/dellemc/openmanage/plugins/module_utils/dellemc_idrac.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -# Dell EMC OpenManage Ansible Modules -# Version 5.0.1 +# Dell OpenManage Ansible Modules +# Version 7.1.0 # Copyright (C) 2019-2022 Dell Inc. or its subsidiaries. All Rights Reserved. # Redistribution and use in source and binary forms, with or without modification, @@ -29,10 +29,10 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type import os +from ansible.module_utils.common.parameters import env_fallback try: from omsdk.sdkinfra import sdkinfra from omsdk.sdkcreds import UserCredentials - from omsdk.sdkfile import FileOnShare, file_share_manager from omsdk.sdkprotopref import ProtoPreference, ProtocolEnum from omsdk.http.sdkwsmanbase import WsManOptions HAS_OMSDK = True @@ -42,8 +42,8 @@ except ImportError: idrac_auth_params = { "idrac_ip": {"required": True, "type": 'str'}, - "idrac_user": {"required": True, "type": 'str'}, - "idrac_password": {"required": True, "type": 'str', "aliases": ['idrac_pwd'], "no_log": True}, + "idrac_user": {"required": True, "type": 'str', "fallback": (env_fallback, ['IDRAC_USERNAME'])}, + "idrac_password": {"required": True, "type": 'str', "aliases": ['idrac_pwd'], "no_log": True, "fallback": (env_fallback, ['IDRAC_PASSWORD'])}, "idrac_port": {"required": False, "default": 443, "type": 'int'}, "validate_certs": {"type": "bool", "default": True}, "ca_path": {"type": "path"}, @@ -55,7 +55,7 @@ class iDRACConnection: def __init__(self, module_params): if not HAS_OMSDK: - raise ImportError("Dell EMC OMSDK library is required for this module") + raise ImportError("Dell OMSDK library is required for this module") self.idrac_ip = module_params['idrac_ip'] self.idrac_user = module_params['idrac_user'] self.idrac_pwd = module_params['idrac_password'] @@ -72,7 +72,7 @@ class iDRACConnection: self.ca_path = self._get_omam_ca_env() verify_ssl = self.ca_path timeout = module_params.get("timeout", 30) - if not timeout or type(timeout) != int: + if not timeout or not isinstance(timeout, int): timeout = 30 self.pOp = WsManOptions(port=self.idrac_port, read_timeout=timeout, verify_ssl=verify_ssl) self.sdk = sdkinfra() @@ -81,6 +81,7 @@ class iDRACConnection: raise RuntimeError(msg) def __enter__(self): + self.idrac_ip = self.idrac_ip.strip('[]') self.sdk.importPath() protopref = ProtoPreference(ProtocolEnum.WSMAN) protopref.include_only(ProtocolEnum.WSMAN) diff --git a/ansible_collections/dellemc/openmanage/plugins/module_utils/idrac_redfish.py b/ansible_collections/dellemc/openmanage/plugins/module_utils/idrac_redfish.py index 168c8277d..cf4581e89 100644 --- a/ansible_collections/dellemc/openmanage/plugins/module_utils/idrac_redfish.py +++ b/ansible_collections/dellemc/openmanage/plugins/module_utils/idrac_redfish.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Dell EMC OpenManage Ansible Modules -# Version 5.5.0 -# Copyright (C) 2019-2022 Dell Inc. or its subsidiaries. All Rights Reserved. +# Dell OpenManage Ansible Modules +# Version 8.0.0 +# Copyright (C) 2019-2023 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: @@ -36,11 +36,13 @@ import os from ansible.module_utils.urls import open_url, ConnectionError, SSLValidationError from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError from ansible.module_utils.six.moves.urllib.parse import urlencode +from ansible.module_utils.common.parameters import env_fallback +from ansible_collections.dellemc.openmanage.plugins.module_utils.utils import config_ipv6 idrac_auth_params = { "idrac_ip": {"required": True, "type": 'str'}, - "idrac_user": {"required": True, "type": 'str'}, - "idrac_password": {"required": True, "type": 'str', "aliases": ['idrac_pwd'], "no_log": True}, + "idrac_user": {"required": True, "type": 'str', "fallback": (env_fallback, ['IDRAC_USERNAME'])}, + "idrac_password": {"required": True, "type": 'str', "aliases": ['idrac_pwd'], "no_log": True, "fallback": (env_fallback, ['IDRAC_PASSWORD'])}, "idrac_port": {"required": False, "default": 443, "type": 'int'}, "validate_certs": {"type": "bool", "default": True}, "ca_path": {"type": "path"}, @@ -108,6 +110,7 @@ class iDRACRedfishAPI(object): self.session_id = None self.protocol = 'https' self._headers = {'Content-Type': 'application/json', 'Accept': 'application/json'} + self.ipaddress = config_ipv6(self.ipaddress) def _get_url(self, uri): return "{0}://{1}:{2}{3}".format(self.protocol, self.ipaddress, self.port, uri) @@ -203,7 +206,7 @@ class iDRACRedfishAPI(object): This method fetches the connected server generation. :return: 14, 4.11.11.11 """ - model, firmware_version = None, None + firmware_version = None response = self.invoke_request(MANAGER_URI, 'GET') if response.status_code == 200: generation = int(re.search(r"\d+(?=G)", response.json_data["Model"]).group()) @@ -250,7 +253,7 @@ class iDRACRedfishAPI(object): return response def export_scp(self, export_format=None, export_use=None, target=None, - job_wait=False, share=None): + job_wait=False, share=None, include_in_export="Default"): """ This method exports system configuration details from the system. :param export_format: XML or JSON. @@ -275,6 +278,21 @@ class iDRACRedfishAPI(object): payload["ShareParameters"]["Username"] = share["username"] if share.get("password") is not None: payload["ShareParameters"]["Password"] = share["password"] + if share.get("ignore_certificate_warning") is not None: + payload["ShareParameters"]["IgnoreCertificateWarning"] = share["ignore_certificate_warning"] + if share.get("proxy_support") is not None: + payload["ShareParameters"]["ProxySupport"] = share["proxy_support"] + if share.get("proxy_type") is not None: + payload["ShareParameters"]["ProxyType"] = share["proxy_type"] + if share.get("proxy_port") is not None: + payload["ShareParameters"]["ProxyPort"] = share["proxy_port"] + if share.get("proxy_server") is not None: + payload["ShareParameters"]["ProxyServer"] = share["proxy_server"] + if share.get("proxy_username") is not None: + payload["ShareParameters"]["ProxyUserName"] = share["proxy_username"] + if share.get("proxy_password") is not None: + payload["ShareParameters"]["ProxyPassword"] = share["proxy_password"] + payload["IncludeInExport"] = include_in_export response = self.invoke_request(EXPORT_URI, "POST", data=payload) if response.status_code == 202 and job_wait: task_uri = response.headers["Location"] @@ -311,10 +329,21 @@ class iDRACRedfishAPI(object): payload["ShareParameters"]["Username"] = share["username"] if share.get("password") is not None: payload["ShareParameters"]["Password"] = share["password"] + if share.get("ignore_certificate_warning") is not None: + payload["ShareParameters"]["IgnoreCertificateWarning"] = share["ignore_certificate_warning"] + if share.get("proxy_support") is not None: + payload["ShareParameters"]["ProxySupport"] = share["proxy_support"] + if share.get("proxy_type") is not None: + payload["ShareParameters"]["ProxyType"] = share["proxy_type"] + if share.get("proxy_port") is not None: + payload["ShareParameters"]["ProxyPort"] = share["proxy_port"] + if share.get("proxy_server") is not None: + payload["ShareParameters"]["ProxyServer"] = share["proxy_server"] + if share.get("proxy_username") is not None: + payload["ShareParameters"]["ProxyUserName"] = share["proxy_username"] + if share.get("proxy_password") is not None: + payload["ShareParameters"]["ProxyPassword"] = share["proxy_password"] response = self.invoke_request(IMPORT_URI, "POST", data=payload) - if response.status_code == 202 and job_wait: - task_uri = response.headers["Location"] - response = self.wait_for_job_complete(task_uri, job_wait=job_wait) return response def import_preview(self, import_buffer=None, target=None, share=None, job_wait=False): @@ -335,6 +364,20 @@ class iDRACRedfishAPI(object): payload["ShareParameters"]["Username"] = share["username"] if share.get("password") is not None: payload["ShareParameters"]["Password"] = share["password"] + if share.get("ignore_certificate_warning") is not None: + payload["ShareParameters"]["IgnoreCertificateWarning"] = share["ignore_certificate_warning"] + if share.get("proxy_support") is not None: + payload["ShareParameters"]["ProxySupport"] = share["proxy_support"] + if share.get("proxy_type") is not None: + payload["ShareParameters"]["ProxyType"] = share["proxy_type"] + if share.get("proxy_port") is not None: + payload["ShareParameters"]["ProxyPort"] = share["proxy_port"] + if share.get("proxy_server") is not None: + payload["ShareParameters"]["ProxyServer"] = share["proxy_server"] + if share.get("proxy_username") is not None: + payload["ShareParameters"]["ProxyUserName"] = share["proxy_username"] + if share.get("proxy_password") is not None: + payload["ShareParameters"]["ProxyPassword"] = share["proxy_password"] response = self.invoke_request(IMPORT_PREVIEW, "POST", data=payload) if response.status_code == 202 and job_wait: task_uri = response.headers["Location"] @@ -356,6 +399,21 @@ class iDRACRedfishAPI(object): response = self.wait_for_job_complete(task_uri, job_wait=job_wait) return response + def import_preview_scp(self, import_buffer=None, target=None, job_wait=False): + """ + This method imports preview system configuration details to the system. + :param import_buffer: import buffer payload content xml or json format + :param target: IDRAC or NIC or ALL or BIOS or RAID. + :param job_wait: True or False decide whether to wait till the job completion. + :return: json response + """ + payload = {"ImportBuffer": import_buffer, "ShareParameters": {"Target": target}} + response = self.invoke_request(IMPORT_PREVIEW, "POST", data=payload) + if response.status_code == 202 and job_wait: + task_uri = response.headers["Location"] + response = self.wait_for_job_complete(task_uri, job_wait=job_wait) + return response + def get_idrac_local_account_attr(self, idrac_attribues, fqdd=None): """ This method filtered from all the user attributes from the given idrac attributes. diff --git a/ansible_collections/dellemc/openmanage/plugins/module_utils/ome.py b/ansible_collections/dellemc/openmanage/plugins/module_utils/ome.py index cdb5ddf2c..cd0bb6be0 100644 --- a/ansible_collections/dellemc/openmanage/plugins/module_utils/ome.py +++ b/ansible_collections/dellemc/openmanage/plugins/module_utils/ome.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Dell EMC OpenManage Ansible Modules -# Version 5.0.1 -# Copyright (C) 2019-2022 Dell Inc. or its subsidiaries. All Rights Reserved. +# Dell OpenManage Ansible Modules +# Version 8.2.0 +# Copyright (C) 2019-2023 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: @@ -34,13 +34,15 @@ import json import os import time from ansible.module_utils.urls import open_url, ConnectionError, SSLValidationError +from ansible.module_utils.common.parameters import env_fallback from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError from ansible.module_utils.six.moves.urllib.parse import urlencode +from ansible_collections.dellemc.openmanage.plugins.module_utils.utils import config_ipv6 ome_auth_params = { "hostname": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": True, "type": "str", "no_log": True}, + "username": {"required": True, "type": "str", "fallback": (env_fallback, ['OME_USERNAME'])}, + "password": {"required": True, "type": "str", "no_log": True, "fallback": (env_fallback, ['OME_PASSWORD'])}, "port": {"type": "int", "default": 443}, "validate_certs": {"type": "bool", "default": True}, "ca_path": {"type": "path"}, @@ -54,6 +56,7 @@ SESSION_RESOURCE_COLLECTION = { JOB_URI = "JobService/Jobs({job_id})" JOB_SERVICE_URI = "JobService/Jobs" +HOST_UNRESOLVED_MSG = "Unable to resolve hostname or IP {0}." class OpenURLResponse(object): @@ -90,7 +93,7 @@ class RestOME(object): def __init__(self, module_params=None, req_session=False): self.module_params = module_params - self.hostname = self.module_params["hostname"] + self.hostname = str(self.module_params["hostname"]).strip('][') self.username = self.module_params["username"] self.password = self.module_params["password"] self.port = self.module_params["port"] @@ -101,6 +104,7 @@ class RestOME(object): self.session_id = None self.protocol = 'https' self._headers = {'Content-Type': 'application/json', 'Accept': 'application/json'} + self.hostname = config_ipv6(self.hostname) def _get_base_url(self): """builds base url""" @@ -261,7 +265,7 @@ class RestOME(object): device_id = device_info["Id"] return {"Id": device_id, "value": device_info} - def get_all_items_with_pagination(self, uri): + def get_all_items_with_pagination(self, uri, query_param=None): """ This implementation mainly to get all available items from ome for pagination supported GET uri @@ -269,7 +273,7 @@ class RestOME(object): :return: dict. """ try: - resp = self.invoke_request('GET', uri) + resp = self.invoke_request('GET', uri, query_param=query_param) data = resp.json_data total_items = data.get("value", []) total_count = data.get('@odata.count', 0) diff --git a/ansible_collections/dellemc/openmanage/plugins/module_utils/redfish.py b/ansible_collections/dellemc/openmanage/plugins/module_utils/redfish.py index 59c467057..8a26eaf60 100644 --- a/ansible_collections/dellemc/openmanage/plugins/module_utils/redfish.py +++ b/ansible_collections/dellemc/openmanage/plugins/module_utils/redfish.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -# Dell EMC OpenManage Ansible Modules -# Version 5.0.1 -# Copyright (C) 2019-2022 Dell Inc. or its subsidiaries. All Rights Reserved. +# Dell OpenManage Ansible Modules +# Version 8.2.0 +# Copyright (C) 2019-2023 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: @@ -34,11 +34,13 @@ import os from ansible.module_utils.urls import open_url, ConnectionError, SSLValidationError from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError from ansible.module_utils.six.moves.urllib.parse import urlencode +from ansible.module_utils.common.parameters import env_fallback +from ansible_collections.dellemc.openmanage.plugins.module_utils.utils import config_ipv6 redfish_auth_params = { "baseuri": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": True, "type": "str", "no_log": True}, + "username": {"required": True, "type": "str", "fallback": (env_fallback, ['IDRAC_USERNAME'])}, + "password": {"required": True, "type": "str", "no_log": True, "fallback": (env_fallback, ['IDRAC_PASSWORD'])}, "validate_certs": {"type": "bool", "default": True}, "ca_path": {"type": "path"}, "timeout": {"type": "int", "default": 30}, @@ -49,6 +51,8 @@ SESSION_RESOURCE_COLLECTION = { "SESSION_ID": "/redfish/v1/Sessions/{Id}", } +HOST_UNRESOLVED_MSG = "Unable to resolve hostname or IP {0}." + class OpenURLResponse(object): """Handles HTTPResponse""" @@ -101,6 +105,7 @@ class Redfish(object): self.protocol = 'https' self.root_uri = '/redfish/v1/' self._headers = {'Content-Type': 'application/json', 'Accept': 'application/json'} + self.hostname = config_ipv6(self.hostname) def _get_base_url(self): """builds base url""" diff --git a/ansible_collections/dellemc/openmanage/plugins/module_utils/utils.py b/ansible_collections/dellemc/openmanage/plugins/module_utils/utils.py index d0da26e57..3d8abfbe5 100644 --- a/ansible_collections/dellemc/openmanage/plugins/module_utils/utils.py +++ b/ansible_collections/dellemc/openmanage/plugins/module_utils/utils.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- # Dell OpenManage Ansible Modules -# Version 6.1.0 -# Copyright (C) 2022 Dell Inc. or its subsidiaries. All Rights Reserved. +# Version 8.2.0 +# Copyright (C) 2022-2023 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: @@ -34,17 +34,25 @@ NO_CHANGES_MSG = "No changes found to be applied." RESET_UNTRACK = "iDRAC reset is in progress. Until the iDRAC is reset, the changes would not apply." RESET_SUCCESS = "iDRAC has been reset successfully." RESET_FAIL = "Unable to reset the iDRAC. For changes to reflect, manually reset the iDRAC." +INVALID_ID_MSG = "Unable to complete the operation because " + \ + "the value `{0}` for the input `{1}` parameter is invalid." SYSTEM_ID = "System.Embedded.1" MANAGER_ID = "iDRAC.Embedded.1" SYSTEMS_URI = "/redfish/v1/Systems" MANAGERS_URI = "/redfish/v1/Managers" +CHASSIS_URI = "/redfish/v1/Chassis" IDRAC_RESET_URI = "/redfish/v1/Managers/{res_id}/Actions/Manager.Reset" SYSTEM_RESET_URI = "/redfish/v1/Systems/{res_id}/Actions/ComputerSystem.Reset" MANAGER_JOB_URI = "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs?$expand=*($levels=1)" MANAGER_JOB_ID_URI = "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs/{0}" - +GET_IDRAC_FIRMWARE_VER_URI = "/redfish/v1/Managers/iDRAC.Embedded.1?$select=FirmwareVersion" +HOSTNAME_REGEX = r"^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$" import time +from datetime import datetime +import re +from ansible.module_utils.six.moves.urllib.error import HTTPError +from ansible.module_utils.urls import ConnectionError, SSLValidationError from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError @@ -69,6 +77,21 @@ def strip_substr_dict(odata_dict, chkstr='@odata.', case_sensitive=False): return odata_dict +def config_ipv6(hostname): + ip_addr, port = hostname, None + if hostname.count(':') == 1: + ip_addr, port = hostname.split(':') + if not re.match(HOSTNAME_REGEX, ip_addr): + if ']:' in ip_addr: + ip_addr, port = ip_addr.split(']:') + ip_addr = ip_addr.strip('[]') + if port is None or port == "": + hostname = "[{0}]".format(ip_addr) + else: + hostname = "[{0}]:{1}".format(ip_addr, port) + return hostname + + def job_tracking(rest_obj, job_uri, max_job_wait_sec=600, job_state_var=('LastRunStatus', 'Id'), job_complete_states=(2060, 2020, 2090), job_fail_states=(2070, 2101, 2102, 2103), job_running_states=(2050, 2040, 2030, 2100), @@ -265,8 +288,8 @@ def reset_idrac(idrac_restobj, wait_time_sec=300, res_id=MANAGER_ID, interval=30 track_failed = True reset_msg = "iDRAC reset triggered successfully." try: - resp = idrac_restobj.invoke_request(IDRAC_RESET_URI.format(res_id=res_id), 'POST', - data={"ResetType": "GracefulRestart"}) + idrac_restobj.invoke_request(IDRAC_RESET_URI.format(res_id=res_id), 'POST', + data={"ResetType": "GracefulRestart"}) if wait_time_sec: track_failed, reset_msg = wait_after_idrac_reset(idrac_restobj, wait_time_sec, interval) reset = True @@ -348,3 +371,157 @@ def get_system_res_id(idrac): res_uri = member[0].get('@odata.id') res_id = res_uri.split("/")[-1] return res_id, error_msg + + +def get_all_data_with_pagination(ome_obj, uri, query_param=None): + """To get all the devices with pagination based on the filter provided.""" + query, resp, report_list = "", None, [] + try: + resp = ome_obj.invoke_request('GET', uri, query_param=query_param) + next_uri = resp.json_data.get("@odata.nextLink", None) + report_list = resp.json_data.get("value") + if query_param is not None: + for k, v in query_param.items(): + query += "{0}={1}".format(k, v.replace(" ", "%20")) + while next_uri is not None: + next_uri_query = "{0}&{1}".format(next_uri.strip("/api"), query) if query else next_uri.strip("/api") + resp = ome_obj.invoke_request('GET', next_uri_query) + report_list.extend(resp.json_data.get("value")) + next_uri = resp.json_data.get("@odata.nextLink", None) + except (URLError, HTTPError, SSLValidationError, ConnectionError, TypeError, ValueError) as err: + raise err + return {"resp_obj": resp, "report_list": report_list} + + +def remove_key(data, regex_pattern='@odata.'): + ''' + :param data: the dict/list to be stripped of unwanted keys + :param remove_char: the substring to be checked among the keys + :return: dict/list + ''' + try: + if isinstance(data, dict): + for key in list(data.keys()): + if re.match(regex_pattern, key): + data.pop(key, None) + else: + remove_key(data[key], regex_pattern) + elif isinstance(data, list): + for item in data: + remove_key(item, regex_pattern) + except Exception: + pass + return data + + +def wait_for_redfish_reboot_job(redfish_obj, res_id, payload=None, wait_time_sec=300): + reset, job_resp, msg = False, {}, "" + try: + resp = redfish_obj.invoke_request('POST', SYSTEM_RESET_URI.format(res_id=res_id), data=payload, api_timeout=120) + time.sleep(10) + if wait_time_sec and resp.status_code == 204: + resp = redfish_obj.invoke_request("GET", MANAGER_JOB_URI) + reboot_job_lst = list(filter(lambda d: (d["JobType"] in ["RebootNoForce"]), resp.json_data["Members"])) + job_resp = max(reboot_job_lst, key=lambda d: datetime.strptime(d["StartTime"], "%Y-%m-%dT%H:%M:%S")) + if job_resp: + reset = True + else: + msg = RESET_FAIL + except Exception: + reset = False + return job_resp, reset, msg + + +def wait_for_redfish_job_complete(redfish_obj, job_uri, job_wait=True, wait_timeout=120, sleep_time=10): + max_sleep_time = wait_timeout + sleep_interval = sleep_time + job_msg = "The job is not complete after {0} seconds.".format(wait_timeout) + job_resp = {} + if job_wait: + while max_sleep_time: + if max_sleep_time > sleep_interval: + max_sleep_time = max_sleep_time - sleep_interval + else: + sleep_interval = max_sleep_time + max_sleep_time = 0 + time.sleep(sleep_interval) + job_resp = redfish_obj.invoke_request("GET", job_uri, api_timeout=120) + if job_resp.json_data.get("PercentComplete") == 100: + time.sleep(10) + return job_resp, "" + if job_resp.json_data.get("JobState") == "RebootFailed": + time.sleep(10) + return job_resp, job_msg + else: + time.sleep(10) + job_resp = redfish_obj.invoke_request("GET", job_uri, api_timeout=120) + return job_resp, "" + return job_resp, job_msg + + +def get_dynamic_uri(idrac_obj, base_uri, search_label=''): + resp = idrac_obj.invoke_request(method='GET', uri=base_uri).json_data + if search_label: + if search_label in resp: + return resp[search_label] + return None + return resp + + +def get_scheduled_job_resp(idrac_obj, job_type): + # job_type can be like 'NICConfiguration' or 'BIOSConfiguration' + job_resp = {} + job_list = idrac_obj.invoke_request( + MANAGER_JOB_URI, "GET").json_data.get('Members', []) + for each_job in job_list: + if each_job.get("JobType") == job_type and each_job.get("JobState") in ["Scheduled", "Running", "Starting"]: + job_resp = each_job + break + return job_resp + + +def delete_job(idrac_obj, job_id): + resp = idrac_obj.invoke_request(uri=MANAGER_JOB_ID_URI.format(job_id), method="DELETE") + return resp.json_data + + +def get_current_time(redfish_obj): + res_id = get_manager_res_id(redfish_obj) + resp = redfish_obj.invoke_request(MANAGERS_URI + '/' + res_id, "GET") + curr_time = resp.json_data.get("DateTime") + date_offset = resp.json_data.get("DateTimeLocalOffset") + return curr_time, date_offset + + +def xml_data_conversion(attr_dict, fqdd=None): + component = """<Component FQDD="{0}">{1}</Component>""" + attr = "" + for k, v in attr_dict.items(): + key = re.sub(r"\.(?!\d)", "#", k) + attr += '<Attribute Name="{0}">{1}</Attribute>'.format(key, v) + root = component.format(fqdd, attr) + return root + + +def validate_and_get_first_resource_id_uri(module, idrac, base_uri): + odata = '@odata.id' + found = False + res_id_uri = None + res_id_input = module.params.get('resource_id') + res_id_members = get_dynamic_uri(idrac, base_uri, 'Members') + for each in res_id_members: + if res_id_input and res_id_input == each[odata].split('/')[-1]: + res_id_uri = each[odata] + found = True + break + if not found and res_id_input: + return res_id_uri, INVALID_ID_MSG.format( + res_id_input, 'resource_id') + elif res_id_input is None: + res_id_uri = res_id_members[0][odata] + return res_id_uri, '' + + +def get_idrac_firmware_version(idrac): + firm_version = idrac.invoke_request(method='GET', uri=GET_IDRAC_FIRMWARE_VER_URI) + return firm_version.json_data.get('FirmwareVersion', '') |