diff options
author | Ansible Core Team <info@ansible.com> | 2020-03-09 09:40:36 +0000 |
---|---|---|
committer | Ansible Core Team <info@ansible.com> | 2020-03-09 09:40:36 +0000 |
commit | a3be23f78125630182c6b2c3e43b5f1d821aea7b (patch) | |
tree | ccea9e5c312615fa4d333dd412b2d478a3cc1029 /lib/ansible/module_utils | |
parent | 48fa7e27b03f0cd5370d8892173a1fbfe6efb220 (diff) | |
download | ansible-a3be23f78125630182c6b2c3e43b5f1d821aea7b.tar.gz |
Migrated to ovirt.ovirt
Diffstat (limited to 'lib/ansible/module_utils')
-rw-r--r-- | lib/ansible/module_utils/ovirt.py | 868 |
1 files changed, 0 insertions, 868 deletions
diff --git a/lib/ansible/module_utils/ovirt.py b/lib/ansible/module_utils/ovirt.py deleted file mode 100644 index 38b048f95e..0000000000 --- a/lib/ansible/module_utils/ovirt.py +++ /dev/null @@ -1,868 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c) 2016 Red Hat, Inc. -# -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see <http://www.gnu.org/licenses/>. -# - -import inspect -import os -import time - -from abc import ABCMeta, abstractmethod -from datetime import datetime -from distutils.version import LooseVersion - -from ansible.module_utils.cloud import CloudRetry -from ansible.module_utils.common._collections_compat import Mapping - -try: - from enum import Enum # enum is a ovirtsdk4 requirement - import ovirtsdk4 as sdk - import ovirtsdk4.version as sdk_version - import ovirtsdk4.types as otypes - HAS_SDK = LooseVersion(sdk_version.VERSION) >= LooseVersion('4.3.0') -except ImportError: - HAS_SDK = False - - -BYTES_MAP = { - 'kib': 2**10, - 'mib': 2**20, - 'gib': 2**30, - 'tib': 2**40, - 'pib': 2**50, -} - - -def check_sdk(module): - if not HAS_SDK: - module.fail_json( - msg='ovirtsdk4 version 4.3.0 or higher is required for this module' - ) - - -def get_dict_of_struct(struct, connection=None, fetch_nested=False, attributes=None): - """ - Convert SDK Struct type into dictionary. - """ - res = {} - - def resolve_href(value): - # Fetch nested values of struct: - try: - value = connection.follow_link(value) - except sdk.Error: - value = None - nested_obj = dict( - (attr, convert_value(getattr(value, attr))) - for attr in attributes if getattr(value, attr, None) is not None - ) - nested_obj['id'] = getattr(value, 'id', None) - nested_obj['href'] = getattr(value, 'href', None) - return nested_obj - - def remove_underscore(val): - if val.startswith('_'): - val = val[1:] - remove_underscore(val) - return val - - def convert_value(value): - nested = False - - if isinstance(value, sdk.Struct): - if not fetch_nested or not value.href: - return get_dict_of_struct(value) - return resolve_href(value) - - elif isinstance(value, Enum) or isinstance(value, datetime): - return str(value) - elif isinstance(value, list) or isinstance(value, sdk.List): - if isinstance(value, sdk.List) and fetch_nested and value.href: - try: - value = connection.follow_link(value) - nested = True - except sdk.Error: - value = [] - - ret = [] - for i in value: - if isinstance(i, sdk.Struct): - if not nested and fetch_nested and i.href: - ret.append(resolve_href(i)) - elif not nested: - ret.append(get_dict_of_struct(i)) - else: - nested_obj = dict( - (attr, convert_value(getattr(i, attr))) - for attr in attributes if getattr(i, attr, None) - ) - nested_obj['id'] = getattr(i, 'id', None) - ret.append(nested_obj) - elif isinstance(i, Enum): - ret.append(str(i)) - else: - ret.append(i) - return ret - else: - return value - - if struct is not None: - for key, value in struct.__dict__.items(): - if value is None: - continue - - key = remove_underscore(key) - res[key] = convert_value(value) - - return res - - -def engine_version(connection): - """ - Return string representation of oVirt engine version. - """ - engine_api = connection.system_service().get() - engine_version = engine_api.product_info.version - return '%s.%s' % (engine_version.major, engine_version.minor) - - -def create_connection(auth): - """ - Create a connection to Python SDK, from task `auth` parameter. - If user doesnt't have SSO token the `auth` dictionary has following parameters mandatory: - url, username, password - - If user has SSO token the `auth` dictionary has following parameters mandatory: - url, token - - The `ca_file` parameter is mandatory in case user want to use secure connection, - in case user want to use insecure connection, it's mandatory to send insecure=True. - - :param auth: dictionary which contains needed values for connection creation - :return: Python SDK connection - """ - - url = auth.get('url') - if url is None and auth.get('hostname') is not None: - url = 'https://{0}/ovirt-engine/api'.format(auth.get('hostname')) - - return sdk.Connection( - url=url, - username=auth.get('username'), - password=auth.get('password'), - ca_file=auth.get('ca_file', None), - insecure=auth.get('insecure', False), - token=auth.get('token', None), - kerberos=auth.get('kerberos', None), - headers=auth.get('headers', None), - ) - - -def convert_to_bytes(param): - """ - This method convert units to bytes, which follow IEC standard. - - :param param: value to be converted - """ - if param is None: - return None - - # Get rid of whitespaces: - param = ''.join(param.split()) - - # Convert to bytes: - if len(param) > 3 and param[-3].lower() in ['k', 'm', 'g', 't', 'p']: - return int(param[:-3]) * BYTES_MAP.get(param[-3:].lower(), 1) - elif param.isdigit(): - return int(param) * 2**10 - else: - raise ValueError( - "Unsupported value(IEC supported): '{value}'".format(value=param) - ) - - -def follow_link(connection, link): - """ - This method returns the entity of the element which link points to. - - :param connection: connection to the Python SDK - :param link: link of the entity - :return: entity which link points to - """ - - if link: - return connection.follow_link(link) - else: - return None - - -def get_link_name(connection, link): - """ - This method returns the name of the element which link points to. - - :param connection: connection to the Python SDK - :param link: link of the entity - :return: name of the entity, which link points to - """ - - if link: - return connection.follow_link(link).name - else: - return None - - -def equal(param1, param2, ignore_case=False): - """ - Compare two parameters and return if they are equal. - This parameter doesn't run equal operation if first parameter is None. - With this approach we don't run equal operation in case user don't - specify parameter in their task. - - :param param1: user inputted parameter - :param param2: value of entity parameter - :return: True if parameters are equal or first parameter is None, otherwise False - """ - if param1 is not None: - if ignore_case: - return param1.lower() == param2.lower() - return param1 == param2 - return True - - -def search_by_attributes(service, list_params=None, **kwargs): - """ - Search for the entity by attributes. Nested entities don't support search - via REST, so in case using search for nested entity we return all entities - and filter them by specified attributes. - """ - list_params = list_params or {} - # Check if 'list' method support search(look for search parameter): - if 'search' in inspect.getargspec(service.list)[0]: - res = service.list( - # There must be double quotes around name, because some oVirt resources it's possible to create then with space in name. - search=' and '.join('{0}="{1}"'.format(k, v) for k, v in kwargs.items()), - **list_params - ) - else: - res = [ - e for e in service.list(**list_params) if len([ - k for k, v in kwargs.items() if getattr(e, k, None) == v - ]) == len(kwargs) - ] - - res = res or [None] - return res[0] - - -def search_by_name(service, name, **kwargs): - """ - Search for the entity by its name. Nested entities don't support search - via REST, so in case using search for nested entity we return all entities - and filter them by name. - - :param service: service of the entity - :param name: name of the entity - :return: Entity object returned by Python SDK - """ - # Check if 'list' method support search(look for search parameter): - if 'search' in inspect.getargspec(service.list)[0]: - res = service.list( - # There must be double quotes around name, because some oVirt resources it's possible to create then with space in name. - search='name="{name}"'.format(name=name) - ) - else: - res = [e for e in service.list() if e.name == name] - - if kwargs: - res = [ - e for e in service.list() if len([ - k for k, v in kwargs.items() if getattr(e, k, None) == v - ]) == len(kwargs) - ] - - res = res or [None] - return res[0] - - -def get_entity(service, get_params=None): - """ - Ignore SDK Error in case of getting an entity from service. - """ - entity = None - try: - if get_params is not None: - entity = service.get(**get_params) - else: - entity = service.get() - except sdk.Error: - # We can get here 404, we should ignore it, in case - # of removing entity for example. - pass - return entity - - -def get_id_by_name(service, name, raise_error=True, ignore_case=False): - """ - Search an entity ID by it's name. - """ - entity = search_by_name(service, name) - - if entity is not None: - return entity.id - - if raise_error: - raise Exception("Entity '%s' was not found." % name) - - -def wait( - service, - condition, - fail_condition=lambda e: False, - timeout=180, - wait=True, - poll_interval=3, -): - """ - Wait until entity fulfill expected condition. - - :param service: service of the entity - :param condition: condition to be fulfilled - :param fail_condition: if this condition is true, raise Exception - :param timeout: max time to wait in seconds - :param wait: if True wait for condition, if False don't wait - :param poll_interval: Number of seconds we should wait until next condition check - """ - # Wait until the desired state of the entity: - if wait: - start = time.time() - while time.time() < start + timeout: - # Exit if the condition of entity is valid: - entity = get_entity(service) - if condition(entity): - return - elif fail_condition(entity): - raise Exception("Error while waiting on result state of the entity.") - - # Sleep for `poll_interval` seconds if none of the conditions apply: - time.sleep(float(poll_interval)) - - raise Exception("Timeout exceed while waiting on result state of the entity.") - - -def __get_auth_dict(): - OVIRT_URL = os.environ.get('OVIRT_URL') - OVIRT_HOSTNAME = os.environ.get('OVIRT_HOSTNAME') - OVIRT_USERNAME = os.environ.get('OVIRT_USERNAME') - OVIRT_PASSWORD = os.environ.get('OVIRT_PASSWORD') - OVIRT_TOKEN = os.environ.get('OVIRT_TOKEN') - OVIRT_CAFILE = os.environ.get('OVIRT_CAFILE') - OVIRT_INSECURE = OVIRT_CAFILE is None - - env_vars = None - if OVIRT_URL is None and OVIRT_HOSTNAME is not None: - OVIRT_URL = 'https://{0}/ovirt-engine/api'.format(OVIRT_HOSTNAME) - if OVIRT_URL and ((OVIRT_USERNAME and OVIRT_PASSWORD) or OVIRT_TOKEN): - env_vars = { - 'url': OVIRT_URL, - 'username': OVIRT_USERNAME, - 'password': OVIRT_PASSWORD, - 'insecure': OVIRT_INSECURE, - 'token': OVIRT_TOKEN, - 'ca_file': OVIRT_CAFILE, - } - if env_vars is not None: - auth = dict(default=env_vars, type='dict') - else: - auth = dict(required=True, type='dict') - - return auth - - -def ovirt_info_full_argument_spec(**kwargs): - """ - Extend parameters of info module with parameters which are common to all - oVirt info modules. - - :param kwargs: kwargs to be extended - :return: extended dictionary with common parameters - """ - spec = dict( - auth=__get_auth_dict(), - fetch_nested=dict(default=False, type='bool'), - nested_attributes=dict(type='list', default=list()), - ) - spec.update(kwargs) - return spec - - -# Left for third-party module compatibility -def ovirt_facts_full_argument_spec(**kwargs): - """ - This is deprecated. Please use ovirt_info_full_argument_spec instead! - - :param kwargs: kwargs to be extended - :return: extended dictionary with common parameters - """ - return ovirt_info_full_argument_spec(**kwargs) - - -def ovirt_full_argument_spec(**kwargs): - """ - Extend parameters of module with parameters which are common to all oVirt modules. - - :param kwargs: kwargs to be extended - :return: extended dictionary with common parameters - """ - spec = dict( - auth=__get_auth_dict(), - timeout=dict(default=180, type='int'), - wait=dict(default=True, type='bool'), - poll_interval=dict(default=3, type='int'), - fetch_nested=dict(default=False, type='bool'), - nested_attributes=dict(type='list', default=list()), - ) - spec.update(kwargs) - return spec - - -def check_params(module): - """ - Most modules must have either `name` or `id` specified. - """ - if module.params.get('name') is None and module.params.get('id') is None: - module.fail_json(msg='"name" or "id" is required') - - -def engine_supported(connection, version): - return LooseVersion(engine_version(connection)) >= LooseVersion(version) - - -def check_support(version, connection, module, params): - """ - Check if parameters used by user are supported by oVirt Python SDK - and oVirt engine. - """ - api_version = LooseVersion(engine_version(connection)) - version = LooseVersion(version) - for param in params: - if module.params.get(param) is not None: - return LooseVersion(sdk_version.VERSION) >= version and api_version >= version - - return True - - -class BaseModule(object): - """ - This is base class for oVirt modules. oVirt modules should inherit this - class and override method to customize specific needs of the module. - The only abstract method of this class is `build_entity`, which must - to be implemented in child class. - """ - __metaclass__ = ABCMeta - - def __init__(self, connection, module, service, changed=False): - self._connection = connection - self._module = module - self._service = service - self._changed = changed - self._diff = {'after': dict(), 'before': dict()} - - @property - def changed(self): - return self._changed - - @changed.setter - def changed(self, changed): - if not self._changed: - self._changed = changed - - @abstractmethod - def build_entity(self): - """ - This method should return oVirt Python SDK type, which we want to - create or update, initialized by values passed by Ansible module. - - For example if we want to create VM, we will return following: - types.Vm(name=self._module.params['vm_name']) - - :return: Specific instance of sdk.Struct. - """ - pass - - def param(self, name, default=None): - """ - Return a module parameter specified by it's name. - """ - return self._module.params.get(name, default) - - def update_check(self, entity): - """ - This method handle checks whether the entity values are same as values - passed to ansible module. By default we don't compare any values. - - :param entity: Entity we want to compare with Ansible module values. - :return: True if values are same, so we don't need to update the entity. - """ - return True - - def pre_create(self, entity): - """ - This method is called right before entity is created. - - :param entity: Entity to be created or updated. - """ - pass - - def post_create(self, entity): - """ - This method is called right after entity is created. - - :param entity: Entity which was created. - """ - pass - - def post_update(self, entity): - """ - This method is called right after entity is updated. - - :param entity: Entity which was updated. - """ - pass - - def diff_update(self, after, update): - for k, v in update.items(): - if isinstance(v, Mapping): - after[k] = self.diff_update(after.get(k, dict()), v) - else: - after[k] = update[k] - return after - - def create( - self, - entity=None, - result_state=None, - fail_condition=lambda e: False, - search_params=None, - update_params=None, - _wait=None, - force_create=False, - **kwargs - ): - """ - Method which is called when state of the entity is 'present'. If user - don't provide `entity` parameter the entity is searched using - `search_params` parameter. If entity is found it's updated, whether - the entity should be updated is checked by `update_check` method. - The corresponding updated entity is build by `build_entity` method. - - Function executed after entity is created can optionally be specified - in `post_create` parameter. Function executed after entity is updated - can optionally be specified in `post_update` parameter. - - :param entity: Entity we want to update, if exists. - :param result_state: State which should entity has in order to finish task. - :param fail_condition: Function which checks incorrect state of entity, if it returns `True` Exception is raised. - :param search_params: Dictionary of parameters to be used for search. - :param update_params: The params which should be passed to update method. - :param kwargs: Additional parameters passed when creating entity. - :return: Dictionary with values returned by Ansible module. - """ - if entity is None and not force_create: - entity = self.search_entity(search_params) - - self.pre_create(entity) - - if entity: - # Entity exists, so update it: - entity_service = self._service.service(entity.id) - if not self.update_check(entity): - new_entity = self.build_entity() - if not self._module.check_mode: - update_params = update_params or {} - updated_entity = entity_service.update( - new_entity, - **update_params - ) - self.post_update(entity) - - # Update diffs only if user specified --diff parameter, - # so we don't useless overload API: - if self._module._diff: - before = get_dict_of_struct( - entity, - self._connection, - fetch_nested=True, - attributes=['name'], - ) - after = before.copy() - self.diff_update(after, get_dict_of_struct(new_entity)) - self._diff['before'] = before - self._diff['after'] = after - - self.changed = True - else: - # Entity don't exists, so create it: - if not self._module.check_mode: - entity = self._service.add( - self.build_entity(), - **kwargs - ) - self.post_create(entity) - self.changed = True - - if not self._module.check_mode: - # Wait for the entity to be created and to be in the defined state: - entity_service = self._service.service(entity.id) - - def state_condition(entity): - return entity - - if result_state: - - def state_condition(entity): - return entity and entity.status == result_state - - wait( - service=entity_service, - condition=state_condition, - fail_condition=fail_condition, - wait=_wait if _wait is not None else self._module.params['wait'], - timeout=self._module.params['timeout'], - poll_interval=self._module.params['poll_interval'], - ) - - return { - 'changed': self.changed, - 'id': getattr(entity, 'id', None), - type(entity).__name__.lower(): get_dict_of_struct( - struct=entity, - connection=self._connection, - fetch_nested=self._module.params.get('fetch_nested'), - attributes=self._module.params.get('nested_attributes'), - ), - 'diff': self._diff, - } - - def pre_remove(self, entity): - """ - This method is called right before entity is removed. - - :param entity: Entity which we want to remove. - """ - pass - - def entity_name(self, entity): - return "{e_type} '{e_name}'".format( - e_type=type(entity).__name__.lower(), - e_name=getattr(entity, 'name', None), - ) - - def remove(self, entity=None, search_params=None, **kwargs): - """ - Method which is called when state of the entity is 'absent'. If user - don't provide `entity` parameter the entity is searched using - `search_params` parameter. If entity is found it's removed. - - Function executed before remove is executed can optionally be specified - in `pre_remove` parameter. - - :param entity: Entity we want to remove. - :param search_params: Dictionary of parameters to be used for search. - :param kwargs: Additional parameters passed when removing entity. - :return: Dictionary with values returned by Ansible module. - """ - if entity is None: - entity = self.search_entity(search_params) - - if entity is None: - return { - 'changed': self.changed, - 'msg': "Entity wasn't found." - } - - self.pre_remove(entity) - - entity_service = self._service.service(entity.id) - if not self._module.check_mode: - entity_service.remove(**kwargs) - wait( - service=entity_service, - condition=lambda entity: not entity, - wait=self._module.params['wait'], - timeout=self._module.params['timeout'], - poll_interval=self._module.params['poll_interval'], - ) - self.changed = True - - return { - 'changed': self.changed, - 'id': entity.id, - type(entity).__name__.lower(): get_dict_of_struct( - struct=entity, - connection=self._connection, - fetch_nested=self._module.params.get('fetch_nested'), - attributes=self._module.params.get('nested_attributes'), - ), - } - - def action( - self, - action, - entity=None, - action_condition=lambda e: e, - wait_condition=lambda e: e, - fail_condition=lambda e: False, - pre_action=lambda e: e, - post_action=lambda e: None, - search_params=None, - **kwargs - ): - """ - This method is executed when we want to change the state of some oVirt - entity. The action to be executed on oVirt service is specified by - `action` parameter. Whether the action should be executed can be - specified by passing `action_condition` parameter. State which the - entity should be in after execution of the action can be specified - by `wait_condition` parameter. - - Function executed before an action on entity can optionally be specified - in `pre_action` parameter. Function executed after an action on entity can - optionally be specified in `post_action` parameter. - - :param action: Action which should be executed by service on entity. - :param entity: Entity we want to run action on. - :param action_condition: Function which is executed when checking if action should be executed. - :param fail_condition: Function which checks incorrect state of entity, if it returns `True` Exception is raised. - :param wait_condition: Function which is executed when waiting on result state. - :param pre_action: Function which is executed before running the action. - :param post_action: Function which is executed after running the action. - :param search_params: Dictionary of parameters to be used for search. - :param kwargs: Additional parameters passed to action. - :return: Dictionary with values returned by Ansible module. - """ - if entity is None: - entity = self.search_entity(search_params) - - entity = pre_action(entity) - - if entity is None: - self._module.fail_json( - msg="Entity not found, can't run action '{0}'.".format( - action - ) - ) - - entity_service = self._service.service(entity.id) - entity = entity_service.get() - if action_condition(entity): - if not self._module.check_mode: - getattr(entity_service, action)(**kwargs) - self.changed = True - - post_action(entity) - - wait( - service=self._service.service(entity.id), - condition=wait_condition, - fail_condition=fail_condition, - wait=self._module.params['wait'], - timeout=self._module.params['timeout'], - poll_interval=self._module.params['poll_interval'], - ) - return { - 'changed': self.changed, - 'id': entity.id, - type(entity).__name__.lower(): get_dict_of_struct( - struct=entity, - connection=self._connection, - fetch_nested=self._module.params.get('fetch_nested'), - attributes=self._module.params.get('nested_attributes'), - ), - 'diff': self._diff, - } - - def wait_for_import(self, condition=lambda e: True): - if self._module.params['wait']: - start = time.time() - timeout = self._module.params['timeout'] - poll_interval = self._module.params['poll_interval'] - while time.time() < start + timeout: - entity = self.search_entity() - if entity and condition(entity): - return entity - time.sleep(poll_interval) - - def search_entity(self, search_params=None, list_params=None): - """ - Always first try to search by `ID`, if ID isn't specified, - check if user constructed special search in `search_params`, - if not search by `name`. - """ - entity = None - - if 'id' in self._module.params and self._module.params['id'] is not None: - entity = get_entity(self._service.service(self._module.params['id']), get_params=list_params) - elif search_params is not None: - entity = search_by_attributes(self._service, list_params=list_params, **search_params) - elif self._module.params.get('name') is not None: - entity = search_by_attributes(self._service, list_params=list_params, name=self._module.params['name']) - - return entity - - def _get_major(self, full_version): - if full_version is None or full_version == "": - return None - if isinstance(full_version, otypes.Version): - return int(full_version.major) - return int(full_version.split('.')[0]) - - def _get_minor(self, full_version): - if full_version is None or full_version == "": - return None - if isinstance(full_version, otypes.Version): - return int(full_version.minor) - return int(full_version.split('.')[1]) - - -def _sdk4_error_maybe(): - """ - Allow for ovirtsdk4 not being installed. - """ - if HAS_SDK: - return sdk.Error - return type(None) - - -class OvirtRetry(CloudRetry): - base_class = _sdk4_error_maybe() - - @staticmethod - def status_code_from_exception(error): - return error.code - - @staticmethod - def found(response_code, catch_extra_error_codes=None): - # This is a list of error codes to retry. - retry_on = [ - # HTTP status: Conflict - 409, - ] - if catch_extra_error_codes: - retry_on.extend(catch_extra_error_codes) - - return response_code in retry_on |