summaryrefslogtreecommitdiffstats
path: root/ansible_collections/dellemc/openmanage/plugins/module_utils
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:52:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:52:35 +0000
commit7fec0b69a082aaeec72fee0612766aa42f6b1b4d (patch)
treeefb569b86ca4da888717f5433e757145fa322e08 /ansible_collections/dellemc/openmanage/plugins/module_utils
parentReleasing progress-linux version 7.7.0+dfsg-3~progress7.99u1. (diff)
downloadansible-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')
-rw-r--r--ansible_collections/dellemc/openmanage/plugins/module_utils/dellemc_idrac.py15
-rw-r--r--ansible_collections/dellemc/openmanage/plugins/module_utils/idrac_redfish.py78
-rw-r--r--ansible_collections/dellemc/openmanage/plugins/module_utils/ome.py20
-rw-r--r--ansible_collections/dellemc/openmanage/plugins/module_utils/redfish.py15
-rw-r--r--ansible_collections/dellemc/openmanage/plugins/module_utils/utils.py187
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', '')