summaryrefslogtreecommitdiffstats
path: root/ansible_collections/openstack/cloud/plugins/module_utils
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:52:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-18 05:52:22 +0000
commit38b7c80217c4e72b1d8988eb1e60bb6e77334114 (patch)
tree356e9fd3762877d07cde52d21e77070aeff7e789 /ansible_collections/openstack/cloud/plugins/module_utils
parentAdding upstream version 7.7.0+dfsg. (diff)
downloadansible-38b7c80217c4e72b1d8988eb1e60bb6e77334114.tar.xz
ansible-38b7c80217c4e72b1d8988eb1e60bb6e77334114.zip
Adding upstream version 9.4.0+dfsg.upstream/9.4.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/openstack/cloud/plugins/module_utils')
-rw-r--r--ansible_collections/openstack/cloud/plugins/module_utils/ironic.py7
-rw-r--r--ansible_collections/openstack/cloud/plugins/module_utils/openstack.py78
-rw-r--r--ansible_collections/openstack/cloud/plugins/module_utils/resource.py237
3 files changed, 261 insertions, 61 deletions
diff --git a/ansible_collections/openstack/cloud/plugins/module_utils/ironic.py b/ansible_collections/openstack/cloud/plugins/module_utils/ironic.py
index a7ab19ef2..44f53560b 100644
--- a/ansible_collections/openstack/cloud/plugins/module_utils/ironic.py
+++ b/ansible_collections/openstack/cloud/plugins/module_utils/ironic.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
@@ -30,8 +33,8 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import o
def ironic_argument_spec(**kwargs):
spec = dict(
- auth_type=dict(required=False),
- ironic_url=dict(required=False),
+ auth_type=dict(),
+ ironic_url=dict(),
)
spec.update(kwargs)
return openstack_full_argument_spec(**spec)
diff --git a/ansible_collections/openstack/cloud/plugins/module_utils/openstack.py b/ansible_collections/openstack/cloud/plugins/module_utils/openstack.py
index 8663d2fca..2f365121e 100644
--- a/ansible_collections/openstack/cloud/plugins/module_utils/openstack.py
+++ b/ansible_collections/openstack/cloud/plugins/module_utils/openstack.py
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by Ansible
@@ -43,41 +46,13 @@ import importlib
import os
from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.six import iteritems
-
-OVERRIDES = {'os_client_config': 'config',
- 'os_endpoint': 'catalog_endpoint',
- 'os_flavor': 'compute_flavor',
- 'os_flavor_info': 'compute_flavor_info',
- 'os_group': 'identity_group',
- 'os_group_info': 'identity_group_info',
- 'os_ironic': 'baremetal_node',
- 'os_ironic_inspect': 'baremetal_inspect',
- 'os_ironic_node': 'baremetal_node_action',
- 'os_keystone_domain': 'identity_domain',
- 'os_keystone_domain_info': 'identity_domain_info',
- 'os_keystone_endpoint': 'endpoint',
- 'os_keystone_identity_provider': 'federation_idp',
- 'os_keystone_identity_provider_info': 'federation_idp_info',
- 'os_keystone_mapping': 'federation_mapping',
- 'os_keystone_mapping_info': 'federation_mapping_info',
- 'os_keystone_role': 'identity_role',
- 'os_keystone_service': 'catalog_service',
- 'os_listener': 'lb_listener',
- 'os_member': 'lb_member',
- 'os_nova_flavor': 'compute_flavor',
- 'os_nova_host_aggregate': 'host_aggregate',
- 'os_pool': 'lb_pool',
- 'os_user': 'identity_user',
- 'os_user_group': 'group_assignment',
- 'os_user_info': 'identity_user_info',
- 'os_user_role': 'role_assignment',
- 'os_zone': 'dns_zone'}
+
+OVERRIDES = {}
CUSTOM_VAR_PARAMS = ['min_ver', 'max_ver']
-MINIMUM_SDK_VERSION = '0.36.0'
-MAXIMUM_SDK_VERSION = '0.98.999'
+MINIMUM_SDK_VERSION = '1.0.0'
+MAXIMUM_SDK_VERSION = None
def ensure_compatibility(version, min_version=None, max_version=None):
@@ -130,7 +105,6 @@ def openstack_argument_spec():
login_username=dict(default=OS_USERNAME),
auth_url=dict(default=OS_AUTH_URL),
region_name=dict(default=OS_REGION_NAME),
- availability_zone=dict(),
)
if OS_PASSWORD:
spec['login_password'] = dict(default=OS_PASSWORD)
@@ -143,39 +117,25 @@ def openstack_argument_spec():
return spec
-def openstack_find_nova_addresses(addresses, ext_tag, key_name=None):
-
- ret = []
- for (k, v) in iteritems(addresses):
- if key_name and k == key_name:
- ret.extend([addrs['addr'] for addrs in v])
- else:
- for interface_spec in v:
- if 'OS-EXT-IPS:type' in interface_spec and interface_spec['OS-EXT-IPS:type'] == ext_tag:
- ret.append(interface_spec['addr'])
- return ret
-
-
def openstack_full_argument_spec(**kwargs):
spec = dict(
- cloud=dict(default=None, type='raw'),
- auth_type=dict(default=None),
- auth=dict(default=None, type='dict', no_log=True),
- region_name=dict(default=None),
- availability_zone=dict(default=None),
- validate_certs=dict(default=None, type='bool', aliases=['verify']),
- ca_cert=dict(default=None, aliases=['cacert']),
- client_cert=dict(default=None, aliases=['cert']),
- client_key=dict(default=None, no_log=True, aliases=['key']),
+ cloud=dict(type='raw'),
+ auth_type=dict(),
+ auth=dict(type='dict', no_log=True),
+ region_name=dict(),
+ validate_certs=dict(type='bool', aliases=['verify']),
+ ca_cert=dict(aliases=['cacert']),
+ client_cert=dict(aliases=['cert']),
+ client_key=dict(no_log=True, aliases=['key']),
wait=dict(default=True, type='bool'),
timeout=dict(default=180, type='int'),
- api_timeout=dict(default=None, type='int'),
+ api_timeout=dict(type='int'),
interface=dict(
default='public', choices=['public', 'internal', 'admin'],
aliases=['endpoint_type']),
- sdk_log_path=dict(default=None, type='str'),
+ sdk_log_path=dict(),
sdk_log_level=dict(
- default='INFO', type='str', choices=['INFO', 'DEBUG']),
+ default='INFO', choices=['INFO', 'DEBUG']),
)
# Filter out all our custom parameters before passing to AnsibleModule
kwargs_copy = copy.deepcopy(kwargs)
@@ -345,7 +305,7 @@ class OpenStackModule:
"The '%s' module has been renamed to '%s' in openstack "
"collection: openstack.cloud.%s" % (
self.module_name, new_module_name, new_module_name),
- version='2.0.0', collection_name='openstack.cloud')
+ version='3.0.0', collection_name='openstack.cloud')
def openstack_cloud_from_module(self):
"""Sets up connection to cloud using provided options. Checks if all
diff --git a/ansible_collections/openstack/cloud/plugins/module_utils/resource.py b/ansible_collections/openstack/cloud/plugins/module_utils/resource.py
new file mode 100644
index 000000000..7f40de383
--- /dev/null
+++ b/ansible_collections/openstack/cloud/plugins/module_utils/resource.py
@@ -0,0 +1,237 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2023 Jakob Meng, <jakobmeng@web.de>
+# Copyright (c) 2023 Red Hat, Inc.
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+class StateMachine:
+
+ @staticmethod
+ def default_crud_functions(connection, service_name, type_name):
+ session = getattr(connection, service_name)
+
+ create_function = getattr(session, 'create_{0}'.format(type_name))
+ delete_function = getattr(session, 'delete_{0}'.format(type_name))
+ find_function = getattr(session, 'find_{0}'.format(type_name))
+ get_function = getattr(session, 'get_{0}'.format(type_name))
+ list_function = getattr(session, '{0}s'.format(type_name))
+ update_function = getattr(session, 'update_{0}'.format(type_name))
+
+ return dict(
+ create=create_function,
+ delete=delete_function,
+ find=find_function,
+ get=get_function,
+ list=list_function,
+ update=update_function,
+ )
+
+ def __init__(self,
+ connection,
+ sdk,
+ type_name,
+ service_name,
+ crud_functions=None,
+ **kwargs):
+ for k in ['connection', 'sdk', 'service_name', 'type_name']:
+ setattr(self, k, locals()[k])
+
+ self.session = getattr(connection, service_name)
+
+ if not crud_functions:
+ crud_functions = StateMachine.default_crud_functions(
+ connection, service_name, type_name)
+
+ for k in ['create', 'delete', 'find', 'get', 'list', 'update']:
+ setattr(self, '{0}_function'.format(k), crud_functions[k])
+
+ # kwargs is for passing arguments to subclasses
+ for k, v in kwargs.items():
+ setattr(self, k, v)
+
+ def __call__(self, attributes, check_mode, state, timeout, wait,
+ updateable_attributes, non_updateable_attributes, **kwargs):
+ # kwargs is for passing arguments to subclasses
+
+ resource = self._find(attributes, **kwargs)
+
+ if check_mode:
+ return self._simulate(state, resource, attributes, timeout, wait,
+ updateable_attributes,
+ non_updateable_attributes, **kwargs)
+
+ if state == 'present' and not resource:
+ # Create resource
+ resource = self._create(attributes, timeout, wait, **kwargs)
+ return resource, True
+
+ elif state == 'present' and resource:
+ # Update resource
+ update = self._build_update(resource, attributes,
+ updateable_attributes,
+ non_updateable_attributes, **kwargs)
+ if update:
+ resource = self._update(resource, timeout, update, wait,
+ **kwargs)
+
+ return resource, bool(update)
+
+ elif state == 'absent' and resource:
+ # Delete resource
+ self._delete(resource, attributes, timeout, wait, **kwargs)
+ return None, True
+
+ elif state == 'absent' and not resource:
+ # Do nothing
+ return None, False
+
+ def _build_update(self, resource, attributes, updateable_attributes,
+ non_updateable_attributes, **kwargs):
+ update = {}
+
+ # Fetch details to populate all resource attributes
+ resource = self.get_function(resource['id'])
+
+ comparison_attributes = (
+ set(updateable_attributes
+ if updateable_attributes is not None
+ else attributes.keys())
+ - set(non_updateable_attributes
+ if non_updateable_attributes is not None
+ else []))
+
+ resource_attributes = dict(
+ (k, attributes[k])
+ for k in comparison_attributes
+ if not self._is_equal(attributes[k], resource[k]))
+
+ if resource_attributes:
+ update['resource_attributes'] = resource_attributes
+
+ return update
+
+ def _create(self, attributes, timeout, wait, **kwargs):
+ resource = self.create_function(**attributes)
+
+ if wait:
+ resource = self.sdk.resource.wait_for_status(self.session,
+ resource,
+ status='active',
+ failures=['error'],
+ wait=timeout,
+ attribute='status')
+
+ return resource
+
+ def _delete(self, resource, attributes, timeout, wait, **kwargs):
+ self.delete_function(resource['id'])
+
+ if wait:
+ for count in self.sdk.utils.iterate_timeout(
+ timeout=timeout,
+ message="Timeout waiting for resource to be absent"
+ ):
+ if self._find(attributes) is None:
+ break
+
+ def _freeze(self, o):
+ if isinstance(o, dict):
+ return frozenset((k, self._freeze(v)) for k, v in o.items())
+
+ if isinstance(o, list):
+ return tuple(self._freeze(v) for v in o)
+
+ return o
+
+ def _is_equal(self, a, b):
+ if any([a is None and b is not None,
+ a is not None and b is None]):
+ return False
+
+ if a is None and b is None:
+ return True
+
+ if isinstance(a, list) and isinstance(b, list):
+ return self._freeze(a) == self._freeze(b)
+
+ if isinstance(a, dict) and isinstance(b, dict):
+ if set(a.keys()) != set(b.keys()):
+ return False
+ return self._freeze(a) == self._freeze(b)
+
+ # else
+ return a == b
+
+ def _find(self, attributes, **kwargs):
+ # use find_* functions for id instead of get_* functions because
+ # get_* functions raise exceptions when resources cannot be found
+ for k in ['id', 'name']:
+ if k in attributes:
+ return self.find_function(attributes[k])
+
+ matches = list(self._find_matches(attributes, **kwargs))
+ if len(matches) > 1:
+ self.fail_json(msg='Found more than a single resource'
+ ' which matches the given attributes.')
+ elif len(matches) == 1:
+ return matches[0]
+ else: # len(matches) == 0
+ return None
+
+ def _find_matches(self, attributes, **kwargs):
+ return self.list_function(**attributes)
+
+ def _update(self, resource, timeout, update, wait, **kwargs):
+ resource_attributes = update.get('resource_attributes')
+ if resource_attributes:
+ resource = self.update_function(resource['id'],
+ **resource_attributes)
+
+ if wait:
+ resource = self.sdk.resource.wait_for_status(self.session,
+ resource,
+ status='active',
+ failures=['error'],
+ wait=timeout,
+ attribute='status')
+
+ return resource
+
+ def _simulate(self, state, resource, attributes, timeout, wait,
+ updateable_attributes,
+ non_updateable_attributes, **kwargs):
+ if state == 'present' and not resource:
+ resource = self._simulate_create(attributes, timeout, wait,
+ **kwargs)
+ return resource, True
+ elif state == 'present' and resource:
+ update = self._build_update(resource, attributes,
+ updateable_attributes,
+ non_updateable_attributes,
+ **kwargs)
+ if update:
+ resource = self._simulate_update(resource, timeout, update,
+ wait, **kwargs)
+
+ return resource, bool(update)
+ elif state == 'absent' and resource:
+ return None, True
+ else:
+ # state == 'absent' and not resource:
+ return None, False
+
+ def _simulate_create(self, attributes, timeout, wait, **kwargs):
+ class Resource(dict):
+ def to_dict(self, *args, **kwargs):
+ return self
+
+ return Resource(attributes)
+
+ def _simulate_update(self, resource, timeout, update, wait, **kwargs):
+ resource_attributes = update.get('resource_attributes')
+ if resource_attributes:
+ for k, v in resource_attributes.items():
+ resource[k] = v
+
+ return resource