diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-05 16:18:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-05 16:18:41 +0000 |
commit | b643c52cf29ce5bbab738b43290af3556efa1ca9 (patch) | |
tree | 21d5c53d7a9b696627a255777cefdf6f78968824 /ansible_collections/dellemc/openmanage/plugins/module_utils | |
parent | Releasing progress-linux version 9.5.1+dfsg-1~progress7.99u1. (diff) | |
download | ansible-b643c52cf29ce5bbab738b43290af3556efa1ca9.tar.xz ansible-b643c52cf29ce5bbab738b43290af3556efa1ca9.zip |
Merging upstream version 10.0.0+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/dellemc/openmanage/plugins/module_utils')
3 files changed, 346 insertions, 6 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 b2b2240d0..cdd94c3cd 100644 --- a/ansible_collections/dellemc/openmanage/plugins/module_utils/dellemc_idrac.py +++ b/ansible_collections/dellemc/openmanage/plugins/module_utils/dellemc_idrac.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- # Dell OpenManage Ansible Modules -# Version 7.1.0 -# Copyright (C) 2019-2022 Dell Inc. or its subsidiaries. All Rights Reserved. +# Version 9.1.0 +# Copyright (C) 2019-2024 Dell Inc. or its subsidiaries. All Rights Reserved. # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: @@ -30,6 +30,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type import os from ansible.module_utils.common.parameters import env_fallback +from ansible_collections.dellemc.openmanage.plugins.module_utils.utils import compress_ipv6 try: from omsdk.sdkinfra import sdkinfra from omsdk.sdkcreds import UserCredentials @@ -56,7 +57,7 @@ class iDRACConnection: def __init__(self, module_params): if not HAS_OMSDK: raise ImportError("Dell OMSDK library is required for this module") - self.idrac_ip = module_params['idrac_ip'] + self.idrac_ip = compress_ipv6(module_params['idrac_ip']) self.idrac_user = module_params['idrac_user'] self.idrac_pwd = module_params['idrac_password'] self.idrac_port = module_params['idrac_port'] diff --git a/ansible_collections/dellemc/openmanage/plugins/module_utils/session_utils.py b/ansible_collections/dellemc/openmanage/plugins/module_utils/session_utils.py new file mode 100644 index 000000000..4bead057a --- /dev/null +++ b/ansible_collections/dellemc/openmanage/plugins/module_utils/session_utils.py @@ -0,0 +1,322 @@ +# -*- coding: utf-8 -*- + +# Dell OpenManage Ansible Modules +# Version 9.2.0 +# Copyright (C) 2024 Dell Inc. or its subsidiaries. All Rights Reserved. + +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: + +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. + +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json +import os +from ansible.module_utils.urls import open_url +from ansible.module_utils.six.moves.urllib.parse import urlencode +from ansible_collections.dellemc.openmanage.plugins.module_utils.utils import config_ipv6 + +HEADER_TYPE = "application/json" + + +class OpenURLResponse(): + """ + HTTP response handler class. + """ + def __init__(self, resp): + """ + Initializes a new instance of the class. + + Args: + resp (Response): The response object to read the body from. + + Initializes the following instance variables: + - body (bytes): The body of the response, or None if the response is None. + - resp (Response): The response object. + + If the response is not None, the body is set to the content of the response. + """ + self.body = None + self.resp = resp + if self.resp: + self.body = self.resp.read() + + @property + def json_data(self): + """ + Returns the JSON data parsed from the `body` attribute of the object. + + :return: The parsed JSON data. + :raises ValueError: If the `body` attribute cannot be parsed as JSON. + """ + try: + return json.loads(self.body) + except ValueError as exc: + raise ValueError("Unable to parse json") from exc + + @property + def status_code(self): + """ + Get the status code of the response. + + Returns: + int: The status code of the response. + """ + return self.resp.getcode() + + @property + def success(self): + """ + Returns a boolean indicating whether the status code of the response is within the range + of 200-299. + + :return: True if the status code is within the range of 200-299, False otherwise. + :rtype: bool + """ + status = self.status_code + return status >= 200 & status <= 299 + + @property + def headers(self): + """ + Returns the headers of the response object. + + :return: A dictionary containing the headers of the response object. + :rtype: dict + """ + return self.resp.headers + + @property + def reason(self): + """ + Get the reason for the response. + + Returns: + str: The reason for the response. + """ + return self.resp.reason + + +class SessionAPI(): + """ + Main class for session operations. + """ + def __init__(self, module_params): + """ + Initializes the object with the given module parameters. + + Args: + module_params (dict): A dictionary containing the module parameters. + - "hostname" (str): The IP address or hostname of the target system. + - "username" (str): The username for authentication. + - "password" (str): The password for authentication. + - "port" (int, optional): The port number. Defaults to None. + - "validate_certs" (bool, optional): Whether to validate SSL certificates. Defaults + to False. + - "ca_path" (str, optional): The path to the CA certificate file. Defaults to None. + - "timeout" (int, optional): The timeout value in seconds. Defaults to None. + - "use_proxy" (bool, optional): Whether to use a proxy. Defaults to True. + + Returns: + None + """ + self.ipaddress = module_params.get("hostname") + self.username = module_params.get("username") + self.password = module_params.get("password") + self.port = module_params.get("port") + self.validate_certs = module_params.get("validate_certs", False) + self.ca_path = module_params.get("ca_path") + self.timeout = module_params.get("timeout") + self.use_proxy = module_params.get("use_proxy", True) + self.protocol = 'https' + self.ipaddress = config_ipv6(self.ipaddress) + self.set_headers(module_params) + + def set_headers(self, module_params): + """ + Set the headers for the HTTP request based on the module parameters. + + Parameters: + module_params (dict): The module parameters containing the state and auth_token. + + Returns: + None + + This function sets the headers for the HTTP request based on the state parameter in the + module_params. + If the state is "present", the headers will include 'Content-Type' and 'Accept' with values + 'application/json'. + If the state is not "present", the headers will include 'Content-Type', 'Accept', and + 'X-Auth-Token' with the value from the auth_token parameter in module_params. + """ + if module_params.get("state") == "present": + self._headers = { + 'Content-Type': HEADER_TYPE, + 'Accept': HEADER_TYPE + } + else: + self._headers = { + 'Content-Type': HEADER_TYPE, + 'Accept': HEADER_TYPE, + 'X-Auth-Token': module_params.get("auth_token") + } + + def _get_url(self, uri): + """ + Generate the full URL by combining the protocol, IP address, port, and URI. + + Parameters: + uri (str): The URI to be appended to the URL. + + Returns: + str: The full URL generated by combining the protocol, IP address, port, and URI. + """ + return f"{self.protocol}://{self.ipaddress}:{self.port}{uri}" + + def _build_url(self, path, query_param=None): + """ + Builds a URL by concatenating the base URI with the given path and query parameters. + + Args: + path (str): The path component of the URL. + query_param (dict, optional): A dictionary of query parameters to be appended to the + URL. Defaults to None. + + Returns: + str: The fully constructed URL. + + Raises: + None + + Examples: + >>> session = SessionUtils() + >>> session._build_url("/api/endpoint", {"param1": "value1", "param2": "value2"}) + "/api/endpoint?param1=value1¶m2=value2" + """ + url = path + base_uri = self._get_url(url) + if path: + url = base_uri + if query_param: + url += f"?{urlencode(query_param)}" + return url + + def _url_common_args_spec(self, method, api_timeout, headers=None): + """ + Generates the common arguments for a URL request. + + Args: + method (str): The HTTP method for the request. + api_timeout (int, optional): The timeout for the API request. If None, the default + timeout is used. + headers (dict, optional): Additional headers to include in the request. + + Returns: + dict: A dictionary containing the common arguments for the URL request. The dictionary + has the following keys: + - method (str): The HTTP method for the request. + - validate_certs (bool): Whether to validate the SSL certificates. + - ca_path (str): The path to the CA certificate bundle. + - use_proxy (bool): Whether to use a proxy for the request. + - headers (dict): The headers to include in the request. + - timeout (int): The timeout for the request. + - follow_redirects (str): The policy for following redirects. + + """ + req_header = self._headers + if headers: + req_header.update(headers) + if api_timeout is None: + api_timeout = self.timeout + if self.ca_path is None: + self.ca_path = self._get_omam_ca_env() + url_kwargs = { + "method": method, + "validate_certs": self.validate_certs, + "ca_path": self.ca_path, + "use_proxy": self.use_proxy, + "headers": req_header, + "timeout": api_timeout, + "follow_redirects": 'all', + } + return url_kwargs + + def _args_session(self, method, api_timeout, headers=None): + """ + Returns a dictionary containing the arguments needed to establish a session. + + :param path: A string representing the path of the API endpoint. + :param method: A string representing the HTTP method to be used. + :param api_timeout: An integer representing the timeout for the API request. + :param headers: An optional dictionary containing additional headers to be included in the + request. + :return: A dictionary containing the arguments needed to establish a session, including the + URL arguments, headers, and API timeout. + """ + req_header = self._headers + if headers: + req_header.update(headers) + url_kwargs = self._url_common_args_spec(method, api_timeout, headers=headers) + return url_kwargs + + def invoke_request(self, uri, method, data=None, query_param=None, headers=None, + api_timeout=None, dump=True): + """ + Invokes a request to the specified URI using the given method and optional parameters. + + :param uri: The URI to send the request to. + :type uri: str + :param method: The HTTP method to use for the request. + :type method: str + :param data: The data to send with the request (default: None). + :type data: dict or None + :param query_param: The query parameters to include in the request URL (default: None). + :type query_param: dict or None + :param headers: The headers to include in the request (default: None). + :type headers: dict or None + :param api_timeout: The timeout for the request in seconds (default: None). + :type api_timeout: int or None + :param dump: Whether to dump the data to JSON before sending the request (default: True). + :type dump: bool + :return: The response data from the request. + :rtype: OpenURLResponse + """ + url_kwargs = self._args_session(method, api_timeout, headers=headers) + if data and dump: + data = json.dumps(data) + url = self._build_url(uri, query_param=query_param) + resp = open_url(url, data=data, **url_kwargs) + resp_data = OpenURLResponse(resp) + return resp_data + + def _get_omam_ca_env(self): + """ + Returns the value of the environment variable REQUESTS_CA_BUNDLE, or if it is not set, + the value of the environment variable CURL_CA_BUNDLE, or if that is not set, + the value of the environment variable OMAM_CA_BUNDLE. + + :return: The value of the environment variable, or None if none of the variables are set. + :rtype: str or None + """ + return (os.environ.get("REQUESTS_CA_BUNDLE") or + os.environ.get("CURL_CA_BUNDLE") or + os.environ.get("OMAM_CA_BUNDLE")) diff --git a/ansible_collections/dellemc/openmanage/plugins/module_utils/utils.py b/ansible_collections/dellemc/openmanage/plugins/module_utils/utils.py index 3d8abfbe5..b838197e0 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 8.2.0 -# Copyright (C) 2022-2023 Dell Inc. or its subsidiaries. All Rights Reserved. +# Version 9.1.0 +# Copyright (C) 2022-2024 Dell Inc. or its subsidiaries. All Rights Reserved. # Redistribution and use in source and binary forms, with or without modification, # are permitted provided that the following conditions are met: @@ -85,6 +85,7 @@ def config_ipv6(hostname): if ']:' in ip_addr: ip_addr, port = ip_addr.split(']:') ip_addr = ip_addr.strip('[]') + ip_addr = compress_ipv6(ip_addr) if port is None or port == "": hostname = "[{0}]".format(ip_addr) else: @@ -92,6 +93,20 @@ def config_ipv6(hostname): return hostname +def compress_ipv6(ipv6_long): + groups = ipv6_long.split(':') + temp = [] + for group in groups: + group = re.sub(r'^0+', '', group) + group = group.lower() + if 0 == len(group): + group = '0' + temp.append(group) + tempstr = ':'.join(temp) + ipv6_short = re.sub(r'(:0)+', ':', tempstr, 1) + return ipv6_short + + 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), @@ -493,12 +508,14 @@ def get_current_time(redfish_obj): return curr_time, date_offset -def xml_data_conversion(attr_dict, fqdd=None): +def xml_data_conversion(attr_dict, fqdd=None, custom_payload_to_add=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) + if custom_payload_to_add: + attr += custom_payload_to_add root = component.format(fqdd, attr) return root |