Skip to content

Commit

Permalink
[OMEVV]: Utils file (#739)
Browse files Browse the repository at this point in the history
* Adding OMEVV utils file
  • Loading branch information
ABHISHEK-SINHA10 authored Oct 13, 2024
1 parent 2c5ad3f commit 08b60f8
Show file tree
Hide file tree
Showing 5 changed files with 597 additions and 0 deletions.
64 changes: 64 additions & 0 deletions plugins/doc_fragments/omevv_auth_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-

#
# Dell OpenManage Ansible Modules
# Version 9.8.0
# Copyright (C) 2020-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


class ModuleDocFragment(object):

DOCUMENTATION = r'''
options:
hostname:
description: OpenManage Enterprise or OpenManage Enterprise Modular IP address or hostname.
type: str
required: true
vcenter_username:
description:
- OpenManage Enterprise Integration for VMware vCenter username.
- 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.
- 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.
- "Example: export OMEVV_VCENTER_UUID=uuid"
type: str
required: false
port:
description: OpenManage Enterprise HTTPS port.
type: int
default: 443
validate_certs:
description: Whether to check SSL certificate.
- If C(true), the SSL certificates will be validated.
- If C(false), the SSL certificates will not be validated.
type: bool
default: true
ca_path:
description:
- The Privacy Enhanced Mail (PEM) file that contains a CA certificate to be used for the validation.
type: path
timeout:
description: The socket level timeout in seconds.
type: int
default: 30
'''
110 changes: 110 additions & 0 deletions plugins/module_utils/omevv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# -*- coding: utf-8 -*-

# Dell OpenManage Ansible Modules
# Version 9.8.0
# Copyright (C) 2024 Dell Inc. or its subsidiaries. All Rights Reserved.

# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:

# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.

# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#


from __future__ import (absolute_import, division, print_function)

__metaclass__ = type

from ansible_collections.dellemc.openmanage.plugins.module_utils.rest_api import RestAPI
from ansible.module_utils.common.parameters import env_fallback
from ansible.module_utils.basic import AnsibleModule

root_omevv_uri = "/omevv/GatewayService/v1"


class RestOMEVV(RestAPI):
def __init__(self, module_params, protocol="https",
root_uri=root_omevv_uri):
super().__init__(
root_uri=root_uri,
module_params=module_params,
req_session=False,
protocol=protocol
)
self.username = module_params.get("vcenter_username")
self.password = module_params.get("vcenter_password")
self.uuid = module_params.get("vcenter_uuid", "")

def invoke_request(self, method, path, data=None, query_param=None, headers=None,
api_timeout=None, dump=True):
"""
Sends a request through the base invoke request method.
Args:
method (str): The HTTP method to use for the request.
path (str): The path to request without query parameter.
data (dict, optional): The payload to send with the request. Defaults to None.
query_param (dict, optional): The dictionary of query parameters to send with the request. Defaults to None.
headers (dict, optional): The dictionary of HTTP headers to send with the request. Defaults to None.
api_timeout (int, optional): The timeout value for the request. Defaults to None.
dump (bool, optional): Whether to dump the payload data. Defaults to True.
Returns:
Response: The response object from the request.
"""
if self.uuid:
headers = headers or {}
headers["x_omivv-api-vcenter-identifier"] = self.uuid
return self._base_invoke_request(method, path, data, query_param, headers,
api_timeout, dump)


class OMEVVAnsibleModule(AnsibleModule):
def __init__(self, argument_spec, bypass_checks=False, no_log=False,
mutually_exclusive=None, required_together=None,
required_one_of=None, add_file_common_args=False,
supports_check_mode=False, required_if=None, required_by=None,
uuid_required=True):
omevv_argument_spec = {
"hostname": {"required": True},
"vcenter_username": {"fallback": (env_fallback, ['OMEVV_VCENTER_USERNAME'])},
"vcenter_password": {"no_log": True, "fallback": (env_fallback, ['OMEVV_VCENTER_PASSWORD'])},
"port": {"default": 443, "type": "int"},
"validate_certs": {"default": True, "type": "bool"},
"ca_path": {"type": "path"},
"timeout": {"default": 30, "type": "int"}
}
argument_spec.update(omevv_argument_spec)
if uuid_required:
argument_spec["vcenter_uuid"] = {"fallback": (env_fallback, ['OMEVV_VCENTER_UUID']), "type": "str"}
auth_required_together = [("vcenter_username", "vcenter_password")]

if mutually_exclusive is None:
mutually_exclusive = []
if required_together is None:
required_together = []
required_together.extend(auth_required_together)
if required_one_of is None:
required_one_of = []
if required_by is None:
required_by = {}

super().__init__(argument_spec, bypass_checks, no_log,
mutually_exclusive, required_together,
required_one_of, add_file_common_args,
supports_check_mode, required_if, required_by)
178 changes: 178 additions & 0 deletions plugins/module_utils/rest_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# -*- coding: utf-8 -*-

# Dell OpenManage Ansible Modules
# Version 9.8.0
# Copyright (C) 2024 Dell Inc. or its subsidiaries. All Rights Reserved.

# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:

# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.

# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#


from __future__ import (absolute_import, division, print_function)

__metaclass__ = type

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


class OpenURLResponse(object):
"""Handles HTTPResponse"""

def __init__(self, response):
"""
Initializes a new instance of the class.
Args:
response (object): The response object to read the body from.
Returns:
None
"""
self.body = None
self.resp = response
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.
Returns:
dict: The parsed JSON data.
Raises:
ValueError: If the `body` attribute cannot be parsed as JSON.
"""
try:
return json.loads(self.body)
except ValueError:
raise ValueError("Unable to parse json")

@property
def status_code(self):
return self.resp.getcode()

@property
def success(self):
status = self.status_code
return status >= 200 & status <= 299

@property
def token_header(self):
return self.resp.headers.get('X-Auth-Token')


class RestAPI:
def __init__(self, root_uri, module_params, req_session=False,
protocol="https", basic_headers=None):
self.hostname = config_ipv6(str(module_params.get("hostname", "")).strip(']['))
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")
self.ca_path = module_params.get("ca_path")
self.timeout = module_params.get("timeout")
self.req_session = req_session
self.session_id = None
self.protocol = protocol
self.root_uri = root_uri
self._headers = basic_headers or {}

def __build_url(self, path, query_param=None):
url = '{0}://{1}:{2}'.format(self.protocol, self.hostname, self.port)
if path:
url = '{0}{1}'.format(url, path)
if query_param:
url += "?{0}".format(urlencode(query_param).replace('+', '%20'))
return url

def _get_omam_ca_env(self):
"""Check if the value is set in REQUESTS_CA_BUNDLE or CURL_CA_BUNDLE or OMAM_CA_BUNDLE or returns None"""
return os.environ.get("REQUESTS_CA_BUNDLE") or os.environ.get("CURL_CA_BUNDLE") or os.environ.get("OMAM_CA_BUNDLE")

def _url_common_args_spec(self, method, api_timeout=None, headers=None):
"""Creates an argument common spec"""
base_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
self._headers.update(base_headers)
if isinstance(headers, dict):
self._headers.update(headers)
return {
"method": method,
"validate_certs": self.validate_certs,
"ca_path": self.ca_path or self._get_omam_ca_env(),
"use_proxy": True,
"headers": self._headers,
"timeout": api_timeout or self.timeout,
"follow_redirects": 'all',
}

def _args_without_session(self, method, api_timeout, headers=None):
"""Creates an argument spec in case of basic authentication"""
url_kwargs = self._url_common_args_spec(method, api_timeout, headers=headers)
url_kwargs["url_username"] = self.username
url_kwargs["url_password"] = self.password
url_kwargs["force_basic_auth"] = True
return url_kwargs

def _args_with_session(self, method, api_timeout, headers=None):
"""Creates an argument spec, in case of authentication with session"""
url_kwargs = self._url_common_args_spec(method, api_timeout, headers=headers)
url_kwargs["force_basic_auth"] = False
return url_kwargs

def _base_invoke_request(self, method, path, data=None, query_param=None, headers=None,
api_timeout=None, dump=True, auth_token_header='X-Auth-Token'):
"""
Sends a request through open_url
Returns :class:`OpenURLResponse` object.
:arg method: HTTP verb to use for the request
:arg path: path to request without query parameter
:arg data: (optional) Payload to send with the request
:arg query_param: (optional) Dictionary of query parameter to send with request
:arg headers: (optional) Dictionary of HTTP Headers to send with the
request
:arg api_timeout: (optional) How long to wait for the server to send
data before giving up
:arg dump: (Optional) boolean value for dumping payload data.
:returns: OpenURLResponse
"""
if self.req_session and auth_token_header in self._headers:
url_kwargs = self._args_with_session(method, api_timeout, headers=headers)
else:
url_kwargs = self._args_without_session(method, api_timeout, headers=headers)
if data and dump:
data = json.dumps(data)
path = self.root_uri + path
url = self.__build_url(path, query_param=query_param)
resp = open_url(url, data=data, **url_kwargs)
resp_data = OpenURLResponse(resp)
return resp_data

def __enter__(self):
return self

def __exit__(self, exc_type, exc_value, traceback):
return False
Loading

0 comments on commit 08b60f8

Please sign in to comment.