From 1bd98462eaf8f5ad02ed1abdc3c3e2ca35763d7e Mon Sep 17 00:00:00 2001 From: Radu Mocanu Date: Fri, 12 Jan 2024 17:40:52 +0200 Subject: [PATCH] refactor: changed more modules, fixed case for multiple user agents --- plugins/module_utils/common_ionos_module.py | 6 +- plugins/modules/lan.py | 5 +- plugins/modules/mongo_cluster.py | 688 ++++++------------ plugins/modules/mongo_cluster_user.py | 476 ++++-------- plugins/modules/nat_gateway.py | 482 ++++-------- plugins/modules/nat_gateway_flowlog.py | 511 ++++--------- plugins/modules/nat_gateway_rule.py | 571 +++++---------- plugins/modules/network_load_balancer.py | 476 ++++-------- .../modules/network_load_balancer_flowlog.py | 506 ++++--------- plugins/modules/network_load_balancer_rule.py | 617 +++++----------- plugins/modules/nic.py | 588 +++++---------- plugins/modules/nic_flowlog.py | 553 +++++--------- plugins/modules/pcc.py | 395 +++------- 13 files changed, 1714 insertions(+), 4160 deletions(-) diff --git a/plugins/module_utils/common_ionos_module.py b/plugins/module_utils/common_ionos_module.py index 1a5f2548..d8889968 100644 --- a/plugins/module_utils/common_ionos_module.py +++ b/plugins/module_utils/common_ionos_module.py @@ -1,5 +1,3 @@ -import re - from ansible.module_utils._text import to_native from .common_ionos_methods import ( @@ -134,8 +132,8 @@ def absent_object(self, clients): def main(self): state = self.module.params.get('state') clients = [sdk.ApiClient(get_sdk_config(self.module, sdk)) for sdk in self.sdks] - for client in clients: - client.user_agent = self.user_agent + for i, client in enumerate(clients): + client.user_agent = self.user_agents[i] check_required_arguments(self.module, state, self.object_name, self.options) try: diff --git a/plugins/modules/lan.py b/plugins/modules/lan.py index d02b6227..bb9791f1 100644 --- a/plugins/modules/lan.py +++ b/plugins/modules/lan.py @@ -3,7 +3,6 @@ # 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 -import re HAS_SDK = True @@ -12,12 +11,11 @@ from ionoscloud import __version__ as sdk_version from ionoscloud.models import Lan, LanProperties from ionoscloud.rest import ApiException - from ionoscloud import ApiClient except ImportError: HAS_SDK = False from ansible import __version__ -from ansible.module_utils.basic import AnsibleModule, env_fallback +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_module import CommonIonosModule @@ -282,7 +280,6 @@ def _remove_object(self, existing_object, clients): self.module.fail_json(msg="failed to remove the LAN: %s" % to_native(e)) - if __name__ == '__main__': ionos_module = LanModule() if not HAS_SDK: diff --git a/plugins/modules/mongo_cluster.py b/plugins/modules/mongo_cluster.py index c74c142f..1326abd8 100644 --- a/plugins/modules/mongo_cluster.py +++ b/plugins/modules/mongo_cluster.py @@ -1,11 +1,6 @@ -import copy -from operator import mod -import yaml - from ansible import __version__ -from ansible.module_utils.basic import AnsibleModule, env_fallback +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native -import re HAS_SDK = True try: @@ -14,14 +9,21 @@ except ImportError: HAS_SDK = False +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_module import CommonIonosModule +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_methods import ( + get_module_arguments, get_resource_id, +) +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_options import get_default_options + + ANSIBLE_METADATA = { 'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community', } -USER_AGENT = 'ansible-module/%s_ionos-cloud-sdk-python/%s' % (__version__, ionoscloud.__version__) -DBAAS_MONGO_USER_AGENT = 'ansible-module/%s_ionos-cloud-sdk-python-dbaas-mongo/%s' % ( +CLOUDAPI_USER_AGENT = 'ansible-module/%s_ionos-cloud-sdk-python/%s' % (__version__, ionoscloud.__version__) +USER_AGENT = 'ansible-module/%s_ionos-cloud-sdk-python-dbaas-mongo/%s' % ( __version__, ionoscloud_dbaas_mongo.__version__) DOC_DIRECTORY = 'dbaas-mongo' STATES = ['present', 'absent', 'update', 'restore'] @@ -91,16 +93,7 @@ { "name": "mongo_db_version", "note": "" }, ] - -def transform_for_documentation(val): - val['required'] = len(val.get('required', [])) == len(STATES) - del val['available'] - del val['type'] - return val - - -DOCUMENTATION = ''' ---- +DOCUMENTATION = """ module: mongo_cluster short_description: Allows operations with Ionos Cloud Mongo Clusters. description: @@ -114,7 +107,7 @@ def transform_for_documentation(val): - "ionoscloud-dbaas-mongo >= 1.0.0" author: - "IONOS Cloud SDK Team " -''' +""" EXAMPLE_PER_STATE = { 'present': '''- name: Create Cluster @@ -161,197 +154,107 @@ def transform_for_documentation(val): """ -def _get_matched_resources(resource_list, identity, identity_paths=None): - """ - Fetch and return a resource based on an identity supplied for it, if none or more than one matches - are found an error is printed and None is returned. - """ - - if identity_paths is None: - identity_paths = [['id'], ['properties', 'name']] - - def check_identity_method(resource): - resource_identity = [] - - for identity_path in identity_paths: - current = resource - for el in identity_path: - current = getattr(current, el) - resource_identity.append(current) - - return identity in resource_identity - - return list(filter(check_identity_method, resource_list.items)) - - -def get_resource(module, resource_list, identity, identity_paths=None): - matched_resources = _get_matched_resources(resource_list, identity, identity_paths) +class MongoClusterModule(CommonIonosModule): + def __init__(self) -> None: + super().__init__() + self.module = AnsibleModule(argument_spec=get_module_arguments(OPTIONS, STATES)) + self.returned_key = RETURNED_KEY + self.object_name = OBJECT_NAME + self.sdks = [ionoscloud_dbaas_mongo, ionoscloud] + self.user_agents = [USER_AGENT, CLOUDAPI_USER_AGENT] + self.options = OPTIONS + self.object_identity_paths = [['id'], ['properties', 'display_name']] + + + def _should_replace_object(self, existing_object, clients): + return ( + self.module.params.get('location') is not None + and existing_object.properties.location != self.module.params.get('location') + or self.module.params.get('mongo_db_version') is not None + and existing_object.properties.mongo_db_version != self.module.params.get('mongo_db_version') + ) - if len(matched_resources) == 1: - return matched_resources[0] - elif len(matched_resources) > 1: - module.fail_json(msg="found more resources of type {} for '{}'".format(resource_list.id, identity)) - else: - return None + def _should_update_object(self, existing_object, clients): + cloudapi_client = clients[1] + datacenter_id = lan_id = cidr_list = None + if self.module.params.get('connections'): + connection = self.module.params.get('connections')[0] + datacenter_list = ionoscloud.DataCentersApi(cloudapi_client).datacenters_get(depth=1) + datacenter_id = get_resource_id(self.module, datacenter_list, connection['datacenter']) + + if datacenter_id is None: + self.module.fail_json('Datacenter {} not found.'.format(connection['datacenter'])) + + lan_id = get_resource_id( + self.module, + ionoscloud.LANsApi(cloudapi_client).datacenters_lans_get(datacenter_id, depth=1), + connection['lan'], + ) + if lan_id is None: + self.module.fail_json('LAN {} not found.'.format(connection['lan'])) + cidr_list = connection['cidr_list'] + + return ( + self.module.params.get('display_name') is not None + and existing_object.properties.display_name != self.module.params.get('display_name') + or self.module.params.get('maintenance_window') is not None + and ( + existing_object.properties.maintenance_window.day_of_the_week != self.module.params.get('maintenance_window').get('day_of_the_week') + or existing_object.properties.maintenance_window.time != self.module.params.get('maintenance_window').get('time') + ) + or self.module.params.get('template_id') is not None + and existing_object.properties.template_id != self.module.params.get('template_id') + or self.module.params.get('instances') is not None + and existing_object.properties.instances != self.module.params.get('instances') + or self.module.params.get('connections') is not None + and ( + existing_object.properties.connections[0].datacenter_id != datacenter_id + or existing_object.properties.connections[0].lan_id != lan_id + or existing_object.properties.connections[0].cidr_list != cidr_list + ) + ) -def get_resource_id(module, resource_list, identity, identity_paths=None): - resource = get_resource(module, resource_list, identity, identity_paths) - return resource.id if resource is not None else None + def _get_object_list(self, clients): + return ionoscloud_dbaas_mongo.ClustersApi(clients[0]).clusters_get() -def _should_replace_object(module, existing_object, _): - return ( - module.params.get('location') is not None - and existing_object.properties.location != module.params.get('location') - or module.params.get('mongo_db_version') is not None - and existing_object.properties.mongo_db_version != module.params.get('mongo_db_version') - ) + def _get_object_name(self): + return self.module.params.get('display_name') -def _should_update_object(module, existing_object, cloudapi_client): - datacenter_id = lan_id = cidr_list = None - if module.params.get('connections'): - connection = module.params.get('connections')[0] - datacenter_list = ionoscloud.DataCentersApi(cloudapi_client).datacenters_get(depth=1) - datacenter_id = get_resource_id(module, datacenter_list, connection['datacenter']) - if datacenter_id is None: - module.fail_json('Datacenter {} not found.'.format(connection['datacenter'])) - - lan_id = get_resource_id( - module, - ionoscloud.LANsApi(cloudapi_client).datacenters_lans_get(datacenter_id, depth=1), - connection['lan'], - ) - if lan_id is None: - module.fail_json('LAN {} not found.'.format(connection['lan'])) - cidr_list = connection['cidr_list'] - - return ( - module.params.get('display_name') is not None - and existing_object.properties.display_name != module.params.get('display_name') - or module.params.get('maintenance_window') is not None - and ( - existing_object.properties.maintenance_window.day_of_the_week != module.params.get('maintenance_window').get('day_of_the_week') - or existing_object.properties.maintenance_window.time != module.params.get('maintenance_window').get('time') - ) - or module.params.get('template_id') is not None - and existing_object.properties.template_id != module.params.get('template_id') - or module.params.get('instances') is not None - and existing_object.properties.instances != module.params.get('instances') - or module.params.get('connections') is not None - and ( - existing_object.properties.connections[0].datacenter_id != datacenter_id - or existing_object.properties.connections[0].lan_id != lan_id - or existing_object.properties.connections[0].cidr_list != cidr_list - ) - ) - - -def _get_object_list(module, client): - return ionoscloud_dbaas_mongo.ClustersApi(client).clusters_get() - - -def _get_object_name(module): - return module.params.get('display_name') - - -def _get_object_identifier(module): - return module.params.get('mongo_cluster') - - -def _create_object(module, dbaas_client, cloudapi_client, existing_object=None): - maintenance_window = module.params.get('maintenance_window') - if maintenance_window: - maintenance_window = dict(module.params.get('maintenance_window')) - maintenance_window['dayOfTheWeek'] = maintenance_window.pop('day_of_the_week') - - if existing_object is not None: - maintenance_window = existing_object.properties.maintenance_window if maintenance_window is None else maintenance_window - - connection = module.params.get('connections')[0] - - datacenter_id = get_resource_id( - module, ionoscloud.DataCentersApi(cloudapi_client).datacenters_get(depth=2), connection['datacenter'], - ) - - if datacenter_id is None: - module.fail_json('Datacenter {} not found.'.format(connection['datacenter'])) - - lan_id = get_resource_id( - module, - ionoscloud.LANsApi(cloudapi_client).datacenters_lans_get(datacenter_id, depth=1), connection['lan'], - ) - - if lan_id is None: - module.fail_json('LAN {} not found.'.format(connection['lan'])) - - connections = [ - ionoscloud_dbaas_mongo.Connection( - datacenter_id=datacenter_id, - lan_id=lan_id, - cidr_list=connection['cidr_list'], - ), - ] - - mongo_cluster_properties = ionoscloud_dbaas_mongo.CreateClusterProperties( - mongo_db_version=module.params.get('mongo_db_version'), - instances=module.params.get('instances'), - connections=connections, - location=module.params.get('location'), - display_name=module.params.get('display_name'), - maintenance_window=maintenance_window, - template_id=module.params.get('template_id'), - ) - mongo_cluster = ionoscloud_dbaas_mongo.CreateClusterRequest(properties=mongo_cluster_properties) - - mongo_clusters_api = ionoscloud_dbaas_mongo.ClustersApi(dbaas_client) - try: - mongo_cluster = mongo_clusters_api.clusters_post(mongo_cluster) - if module.params.get('wait'): - dbaas_client.wait_for( - fn_request=lambda: mongo_clusters_api.clusters_find_by_id(mongo_cluster.id), - fn_check=lambda cluster: cluster and cluster.metadata and cluster.metadata.state == 'AVAILABLE', - scaleup=10000, - timeout=module.params.get('wait_timeout'), - ) - except Exception as e: - module.fail_json(msg="failed to create the Mongo Cluster: %s" % to_native(e)) + def _get_object_identifier(self): + return self.module.params.get('mongo_cluster') - return mongo_cluster + def _create_object(self, existing_object, clients): + dbaas_client = clients[0] + cloudapi_client = clients[1] + maintenance_window = self.module.params.get('maintenance_window') + if maintenance_window: + maintenance_window = dict(self.module.params.get('maintenance_window')) + maintenance_window['dayOfTheWeek'] = maintenance_window.pop('day_of_the_week') -def _update_object(module, dbaas_client, cloudapi_client, existing_object): - clusters_api = ionoscloud_dbaas_mongo.ClustersApi(dbaas_client) - dbaas_client.wait_for( - fn_request=lambda: clusters_api.clusters_find_by_id(existing_object.id), - fn_check=lambda cluster: cluster.metadata.state == 'AVAILABLE', - scaleup=10000, - ) - maintenance_window = module.params.get('maintenance_window') - if maintenance_window: - maintenance_window = dict(module.params.get('maintenance_window')) - maintenance_window['dayOfTheWeek'] = maintenance_window.pop('day_of_the_week') + if existing_object is not None: + maintenance_window = existing_object.properties.maintenance_window if maintenance_window is None else maintenance_window - connections = None - if module.params.get('connections'): - connection = module.params.get('connections')[0] + connection = self.module.params.get('connections')[0] datacenter_id = get_resource_id( - module, ionoscloud.DataCentersApi(cloudapi_client).datacenters_get(depth=2), connection['datacenter'], + self.module, ionoscloud.DataCentersApi(cloudapi_client).datacenters_get(depth=2), connection['datacenter'], ) if datacenter_id is None: - module.fail_json('Datacenter {} not found.'.format(connection['datacenter'])) + self.module.fail_json('Datacenter {} not found.'.format(connection['datacenter'])) lan_id = get_resource_id( - module, + self.module, ionoscloud.LANsApi(cloudapi_client).datacenters_lans_get(datacenter_id, depth=1), connection['lan'], ) if lan_id is None: - module.fail_json('LAN {} not found.'.format(connection['lan'])) + self.module.fail_json('LAN {} not found.'.format(connection['lan'])) connections = [ ionoscloud_dbaas_mongo.Connection( @@ -361,297 +264,168 @@ def _update_object(module, dbaas_client, cloudapi_client, existing_object): ), ] - mongo_cluster_properties = ionoscloud_dbaas_mongo.PatchClusterProperties( - instances=module.params.get('instances'), - display_name=module.params.get('display_name'), - maintenance_window=maintenance_window, - connections=connections, - template_id=module.params.get('template_id'), - ) - mongo_cluster = ionoscloud_dbaas_mongo.PatchClusterRequest(properties=mongo_cluster_properties) - - try: - mongo_cluster = clusters_api.clusters_patch( - cluster_id=existing_object.id, - patch_cluster_request=mongo_cluster, + mongo_cluster_properties = ionoscloud_dbaas_mongo.CreateClusterProperties( + mongo_db_version=self.module.params.get('mongo_db_version'), + instances=self.module.params.get('instances'), + connections=connections, + location=self.module.params.get('location'), + display_name=self.module.params.get('display_name'), + maintenance_window=maintenance_window, + template_id=self.module.params.get('template_id'), ) + mongo_cluster = ionoscloud_dbaas_mongo.CreateClusterRequest(properties=mongo_cluster_properties) - if module.params.get('wait'): - dbaas_client.wait_for( - fn_request=lambda: clusters_api.clusters_find_by_id(mongo_cluster.id), - fn_check=lambda cluster: cluster.metadata.state == 'AVAILABLE', - scaleup=10000, - timeout=module.params.get('wait_timeout'), - ) - - except Exception as e: - module.fail_json(msg="failed to update the Mongo Cluster: %s" % to_native(e)) - return mongo_cluster - - -def _remove_object(module, dbaas_client, existing_object): - clusters_api = ionoscloud_dbaas_mongo.ClustersApi(dbaas_client) - - try: - if existing_object.metadata.state != 'DESTROYING': - clusters_api.clusters_delete(existing_object.id) - - if module.params.get('wait'): - try: + mongo_clusters_api = ionoscloud_dbaas_mongo.ClustersApi(dbaas_client) + try: + mongo_cluster = mongo_clusters_api.clusters_post(mongo_cluster) + if self.module.params.get('wait'): dbaas_client.wait_for( - fn_request=lambda: clusters_api.clusters_find_by_id(existing_object.id), - fn_check=lambda _: False, + fn_request=lambda: mongo_clusters_api.clusters_find_by_id(mongo_cluster.id), + fn_check=lambda cluster: cluster and cluster.metadata and cluster.metadata.state == 'AVAILABLE', scaleup=10000, - timeout=module.params.get('wait_timeout'), + timeout=self.module.params.get('wait_timeout'), ) - except ionoscloud_dbaas_mongo.ApiException as e: - if e.status != 404: - raise e - except Exception as e: - module.fail_json(msg="failed to delete the Mongo cluster: %s" % to_native(e)) - - -def update_replace_object(module, dbaas_client, cloudapi_client, existing_object): - if _should_replace_object(module, existing_object, cloudapi_client): - - if not module.params.get('allow_replace'): - module.fail_json(msg="{} should be replaced but allow_replace is set to False.".format(OBJECT_NAME)) - - new_object = _create_object(module, dbaas_client, cloudapi_client, existing_object).to_dict() - _remove_object(module, dbaas_client, existing_object) - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: new_object, - } - if _should_update_object(module, existing_object, cloudapi_client): - # Update - return { - 'changed': True, - 'failed': False, - 'action': 'update', - RETURNED_KEY: _update_object(module, dbaas_client, cloudapi_client, existing_object).to_dict() - } - - # No action - return { - 'changed': False, - 'failed': False, - 'action': 'create', - RETURNED_KEY: existing_object.to_dict() - } - - -def create_object(module, dbaas_client, cloudapi_client): - existing_object = get_resource( - module, _get_object_list(module, dbaas_client), _get_object_name(module), - [['id'], ['properties', 'display_name']], - ) - - if existing_object: - return update_replace_object(module, dbaas_client, cloudapi_client, existing_object) - - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: _create_object(module, dbaas_client, cloudapi_client).to_dict() - } - - -def update_object(module, dbaas_mongodb_api_client, cloudapi_api_client): - object_name = _get_object_name(module) - object_list = _get_object_list(module, dbaas_mongodb_api_client) - - existing_object = get_resource( - module, object_list, _get_object_identifier(module), - [['id'], ['properties', 'display_name']], - ) - - if existing_object is None: - module.exit_json(changed=False) - return - - existing_object_id_by_new_name = get_resource_id( - module, object_list, object_name, - [['id'], ['properties', 'display_name']], - ) - - if ( - existing_object.id is not None - and existing_object_id_by_new_name is not None - and existing_object_id_by_new_name != existing_object.id - ): - module.fail_json( - msg='failed to update the {}: Another resource with the desired name ({}) exists'.format( - OBJECT_NAME, object_name, - ), - ) - - return update_replace_object(module, dbaas_mongodb_api_client, cloudapi_api_client, existing_object) - - -def remove_object(module, client): - - existing_object = get_resource( - module, _get_object_list(module, client), _get_object_identifier(module), - [['id'], ['properties', 'display_name']], - ) + except Exception as e: + self.module.fail_json(msg="failed to create the Mongo Cluster: %s" % to_native(e)) - if existing_object is None: - module.exit_json(changed=False) - return + return mongo_cluster - _remove_object(module, client, existing_object) - return { - 'action': 'delete', - 'changed': True, - 'id': existing_object.id, - } + def _update_object(self, existing_object, clients): + dbaas_client = clients[0] + cloudapi_client = clients[1] + clusters_api = ionoscloud_dbaas_mongo.ClustersApi(dbaas_client) + dbaas_client.wait_for( + fn_request=lambda: clusters_api.clusters_find_by_id(existing_object.id), + fn_check=lambda cluster: cluster.metadata.state == 'AVAILABLE', + scaleup=10000, + ) + maintenance_window = self.module.params.get('maintenance_window') + if maintenance_window: + maintenance_window = dict(self.module.params.get('maintenance_window')) + maintenance_window['dayOfTheWeek'] = maintenance_window.pop('day_of_the_week') + connections = None + if self.module.params.get('connections'): + connection = self.module.params.get('connections')[0] -def restore_object(module, dbaas_client): - mongo_cluster_api = ionoscloud_dbaas_mongo.ClustersApi(dbaas_client) + datacenter_id = get_resource_id( + self.module, ionoscloud.DataCentersApi(cloudapi_client).datacenters_get(depth=2), connection['datacenter'], + ) - mongo_cluster_id = get_resource_id( - module, - mongo_cluster_api.clusters_get(), - module.params.get('mongo_cluster'), - [['id'], ['properties', 'display_name']], - ) + if datacenter_id is None: + self.module.fail_json('Datacenter {} not found.'.format(connection['datacenter'])) + + lan_id = get_resource_id( + self.module, + ionoscloud.LANsApi(cloudapi_client).datacenters_lans_get(datacenter_id, depth=1), connection['lan'], + ) - restore_request = ionoscloud_dbaas_mongo.CreateRestoreRequest( - backup_id=module.params.get('backup_id'), - ) + if lan_id is None: + self.module.fail_json('LAN {} not found.'.format(connection['lan'])) - try: - ionoscloud_dbaas_mongo.RestoresApi(dbaas_client).clusters_restore_post(mongo_cluster_id, restore_request) + connections = [ + ionoscloud_dbaas_mongo.Connection( + datacenter_id=datacenter_id, + lan_id=lan_id, + cidr_list=connection['cidr_list'], + ), + ] + + mongo_cluster_properties = ionoscloud_dbaas_mongo.PatchClusterProperties( + instances=self.module.params.get('instances'), + display_name=self.module.params.get('display_name'), + maintenance_window=maintenance_window, + connections=connections, + template_id=self.module.params.get('template_id'), + ) + mongo_cluster = ionoscloud_dbaas_mongo.PatchClusterRequest(properties=mongo_cluster_properties) - if module.params.get('wait'): - dbaas_client.wait_for( - fn_request=lambda: mongo_cluster_api.clusters_find_by_id(mongo_cluster_id), - fn_check=lambda cluster: cluster.metadata.state == 'AVAILABLE', - scaleup=10000, - timeout=module.params.get('wait_timeout'), + try: + mongo_cluster = clusters_api.clusters_patch( + cluster_id=existing_object.id, + patch_cluster_request=mongo_cluster, ) - return { - 'action': 'restore', - 'changed': True, - 'id': mongo_cluster_id, - } - except Exception as e: - module.fail_json(msg="failed to restore the Mongo cluster: %s" % to_native(e)) - return { - 'action': 'restore', - 'changed': False, - 'id': mongo_cluster_id, - } - - -def get_module_arguments(): - arguments = {} - - for option_name, option in OPTIONS.items(): - arguments[option_name] = { - 'type': option['type'], - } - for key in ['choices', 'default', 'aliases', 'no_log', 'elements']: - if option.get(key) is not None: - arguments[option_name][key] = option.get(key) - - if option.get('env_fallback'): - arguments[option_name]['fallback'] = (env_fallback, [option['env_fallback']]) - - if len(option.get('required', [])) == len(STATES): - arguments[option_name]['required'] = True - - return arguments - - -def get_sdk_config(module, sdk): - username = module.params.get('username') - password = module.params.get('password') - token = module.params.get('token') - api_url = module.params.get('api_url') - - if token is not None: - # use the token instead of username & password - conf = { - 'token': token - } - else: - # use the username & password - conf = { - 'username': username, - 'password': password, - } - - if api_url is not None: - conf['host'] = api_url - conf['server_index'] = None - - return sdk.Configuration(**conf) - - -def check_required_arguments(module, state, object_name): - # manually checking if token or username & password provided - if ( - not module.params.get("token") - and not (module.params.get("username") and module.params.get("password")) - ): - module.fail_json( - msg='Token or username & password are required for {object_name} state {state}'.format( - object_name=object_name, - state=state, - ), - ) + if self.module.params.get('wait'): + dbaas_client.wait_for( + fn_request=lambda: clusters_api.clusters_find_by_id(mongo_cluster.id), + fn_check=lambda cluster: cluster.metadata.state == 'AVAILABLE', + scaleup=10000, + timeout=self.module.params.get('wait_timeout'), + ) - for option_name, option in OPTIONS.items(): - if state in option.get('required', []) and not module.params.get(option_name): - module.fail_json( - msg='{option_name} parameter is required for {object_name} state {state}'.format( - option_name=option_name, - object_name=object_name, - state=state, - ), - ) + except Exception as e: + self.module.fail_json(msg="failed to update the Mongo Cluster: %s" % to_native(e)) + return mongo_cluster + + + def _remove_object(self, existing_object, clients): + dbaas_client = clients[0] + clusters_api = ionoscloud_dbaas_mongo.ClustersApi(dbaas_client) + + try: + if existing_object.metadata.state != 'DESTROYING': + clusters_api.clusters_delete(existing_object.id) + + if self.module.params.get('wait'): + try: + dbaas_client.wait_for( + fn_request=lambda: clusters_api.clusters_find_by_id(existing_object.id), + fn_check=lambda _: False, + scaleup=10000, + timeout=self.module.params.get('wait_timeout'), + ) + except ionoscloud_dbaas_mongo.ApiException as e: + if e.status != 404: + raise e + except Exception as e: + self.module.fail_json(msg="failed to delete the Mongo cluster: %s" % to_native(e)) + + + def restore_object(self, clients): + dbaas_client = clients[0] + mongo_cluster_api = ionoscloud_dbaas_mongo.ClustersApi(dbaas_client) + + mongo_cluster_id = get_resource_id( + self.module, + mongo_cluster_api.clusters_get(), + self.module.params.get('mongo_cluster'), + [['id'], ['properties', 'display_name']], + ) + restore_request = ionoscloud_dbaas_mongo.CreateRestoreRequest( + backup_id=self.module.params.get('backup_id'), + ) -def main(): - module = AnsibleModule(argument_spec=get_module_arguments(), supports_check_mode=True) + try: + ionoscloud_dbaas_mongo.RestoresApi(dbaas_client).clusters_restore_post(mongo_cluster_id, restore_request) - if not HAS_SDK: - module.fail_json(msg='both ionoscloud and ionoscloud_dbaas_mongo are required for this module, ' - 'run `pip install ionoscloud ionoscloud_dbaas_mongo`') + if self.module.params.get('wait'): + dbaas_client.wait_for( + fn_request=lambda: mongo_cluster_api.clusters_find_by_id(mongo_cluster_id), + fn_check=lambda cluster: cluster.metadata.state == 'AVAILABLE', + scaleup=10000, + timeout=self.module.params.get('wait_timeout'), + ) - cloudapi_api_client = ionoscloud.ApiClient(get_sdk_config(module, ionoscloud)) - cloudapi_api_client.user_agent = USER_AGENT - dbaas_mongo_api_client = ionoscloud_dbaas_mongo.ApiClient(get_sdk_config(module, ionoscloud_dbaas_mongo)) - dbaas_mongo_api_client.user_agent = DBAAS_MONGO_USER_AGENT - - state = module.params.get('state') - - check_required_arguments(module, state, OBJECT_NAME) - - try: - if state == 'present': - module.exit_json(**create_object(module, dbaas_mongo_api_client, cloudapi_api_client)) - elif state == 'absent': - module.exit_json(**remove_object(module, dbaas_mongo_api_client)) - elif state == 'update': - module.exit_json(**update_object(module, dbaas_mongo_api_client, cloudapi_api_client)) - elif state == 'restore': - module.exit_json(**restore_object(module, dbaas_mongo_api_client)) - except Exception as e: - module.fail_json( - msg='failed to set {object_name} state {state}: {error}'.format( - object_name=OBJECT_NAME, error=to_native(e), state=state, - ), - ) + return { + 'action': 'restore', + 'changed': True, + 'id': mongo_cluster_id, + } + except Exception as e: + self.module.fail_json(msg="failed to restore the Mongo cluster: %s" % to_native(e)) + return { + 'action': 'restore', + 'changed': False, + 'id': mongo_cluster_id, + } if __name__ == '__main__': - main() + ionos_module = MongoClusterModule() + if not HAS_SDK: + ionos_module.module.fail_json(msg='both ionoscloud and ionoscloud_dbaas_mongo are required for this module, ' + 'run `pip install ionoscloud ionoscloud_dbaas_mongo`') + ionos_module.main() diff --git a/plugins/modules/mongo_cluster_user.py b/plugins/modules/mongo_cluster_user.py index a9af6cfd..c519945e 100644 --- a/plugins/modules/mongo_cluster_user.py +++ b/plugins/modules/mongo_cluster_user.py @@ -1,11 +1,6 @@ -import copy -from operator import mod -import yaml - from ansible import __version__ -from ansible.module_utils.basic import AnsibleModule, env_fallback +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native -import re HAS_SDK = True try: @@ -14,14 +9,21 @@ except ImportError: HAS_SDK = False +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_module import CommonIonosModule +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_methods import ( + get_module_arguments, get_resource_id, +) +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_options import get_default_options + + ANSIBLE_METADATA = { 'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community', } -USER_AGENT = 'ansible-module/%s_ionos-cloud-sdk-python/%s' % (__version__, ionoscloud.__version__) -DBAAS_MONGO_USER_AGENT = 'ansible-module/%s_ionos-cloud-sdk-python-dbaas-mongo/%s' % ( +CLOUDAPI_USER_AGENT = 'ansible-module/%s_ionos-cloud-sdk-python/%s' % (__version__, ionoscloud.__version__) +USER_AGENT = 'ansible-module/%s_ionos-cloud-sdk-python-dbaas-mongo/%s' % ( __version__, ionoscloud_dbaas_mongo.__version__) DOC_DIRECTORY = 'dbaas-mongo' STATES = ['present', 'update', 'absent'] @@ -62,15 +64,7 @@ } -def transform_for_documentation(val): - val['required'] = len(val.get('required', [])) == len(STATES) - del val['available'] - del val['type'] - return val - - -DOCUMENTATION = ''' ---- +DOCUMENTATION = """ module: mongo_cluster short_description: Allows operations with Ionos Cloud Mongo Cluster Users. description: @@ -84,7 +78,7 @@ def transform_for_documentation(val): - "ionoscloud-dbaas-mongo >= 1.0.0" author: - "IONOS Cloud SDK Team " -''' +""" EXAMPLE_PER_STATE = { 'present': '''- name: Create Cluster User @@ -123,350 +117,150 @@ def transform_for_documentation(val): """ -def _get_matched_resources(resource_list, identity, identity_paths=None): - """ - Fetch and return a resource based on an identity supplied for it, if none or more than one matches - are found an error is printed and None is returned. - """ - - if identity_paths is None: - identity_paths = [['id'], ['properties', 'name']] - - def check_identity_method(resource): - resource_identity = [] - - for identity_path in identity_paths: - current = resource - for el in identity_path: - current = getattr(current, el) - resource_identity.append(current) - - return identity in resource_identity - - return list(filter(check_identity_method, resource_list.items)) +class MongoUserModule(CommonIonosModule): + def __init__(self) -> None: + super().__init__() + self.module = AnsibleModule(argument_spec=get_module_arguments(OPTIONS, STATES)) + self.returned_key = RETURNED_KEY + self.object_name = OBJECT_NAME + self.sdks = [ionoscloud] + self.user_agent = USER_AGENT + self.options = OPTIONS + self.object_identity_paths = [['properties', 'username']] -def get_resource(module, resource_list, identity, identity_paths=None): - matched_resources = _get_matched_resources(resource_list, identity, identity_paths) + def _should_replace_object(self, existing_object, clients): + return False - if len(matched_resources) == 1: - return matched_resources[0] - elif len(matched_resources) > 1: - module.fail_json(msg="found more resources of type {} for '{}'".format(resource_list.id, identity)) - else: - return None - -def get_resource_id(module, resource_list, identity, identity_paths=None): - resource = get_resource(module, resource_list, identity, identity_paths) - return resource.id if resource is not None else None - - -def _should_replace_object(module, existing_object): - return False - - -def _should_update_object(module, existing_object): - def sort_func(el): - return el['database'], el['role'] - - existing_roles = sorted( - [{'role': role.role, 'database': role.database} for role in existing_object.properties.roles], - key=sort_func, - ) - return ( - module.params.get('mongo_password') is not None - and existing_object.properties.password != module.params.get('mongo_password') - or module.params.get('roles') is not None - and existing_roles != sorted(module.params.get('roles'), key=sort_func) - ) + def _should_update_object(self, existing_object, clients): + def sort_func(el): + return el['database'], el['role'] + + existing_roles = sorted( + [{'role': role.role, 'database': role.database} for role in existing_object.properties.roles], + key=sort_func, + ) + return ( + self.module.params.get('mongo_password') is not None + and existing_object.properties.password != self.module.params.get('mongo_password') + or self.module.params.get('roles') is not None + and existing_roles != sorted(self.module.params.get('roles'), key=sort_func) + ) -def _get_object_list(module, client): - mongo_cluster_id = get_resource_id( - module, ionoscloud_dbaas_mongo.ClustersApi(client).clusters_get(), - module.params.get('mongo_cluster'), - [['id'], ['properties', 'display_name']], - ) - return ionoscloud_dbaas_mongo.UsersApi(client).clusters_users_get(mongo_cluster_id) + def _get_object_list(self, clients): + client = clients[0] + mongo_cluster_id = get_resource_id( + self.module, ionoscloud_dbaas_mongo.ClustersApi(client).clusters_get(), + self.module.params.get('mongo_cluster'), + [['id'], ['properties', 'display_name']], + ) + return ionoscloud_dbaas_mongo.UsersApi(client).clusters_users_get(mongo_cluster_id) -def _get_object_name(module): - return module.params.get('mongo_username') + def _get_object_name(self): + return self.module.params.get('mongo_username') -def _get_object_identifier(module): - return module.params.get('mongo_username') + def _get_object_identifier(self): + return self.module.params.get('mongo_username') -def _create_object(module, client, existing_object=None): - mongo_username = module.params.get('mongo_username') - mongo_password = module.params.get('mongo_password') - user_roles = list(map( - lambda role: ionoscloud_dbaas_mongo.UserRoles(role=role['role'], database=role['database']), - module.params.get('user_roles'), - )) + def _create_object(self, existing_object, clients): + client = clients[0] + mongo_username = self.module.params.get('mongo_username') + mongo_password = self.module.params.get('mongo_password') + user_roles = list(map( + lambda role: ionoscloud_dbaas_mongo.UserRoles(role=role['role'], database=role['database']), + self.module.params.get('user_roles'), + )) - if existing_object is not None: - mongo_username = existing_object.properties.username if mongo_username is None else mongo_username - user_roles = existing_object.properties.roles if user_roles is None else user_roles + if existing_object is not None: + mongo_username = existing_object.properties.username if mongo_username is None else mongo_username + user_roles = existing_object.properties.roles if user_roles is None else user_roles - users_api = ionoscloud_dbaas_mongo.UsersApi(client) + users_api = ionoscloud_dbaas_mongo.UsersApi(client) - mongo_user = ionoscloud_dbaas_mongo.User(properties=ionoscloud_dbaas_mongo.UserProperties( - username=mongo_username, - password=mongo_password, - roles=user_roles, - )) + mongo_user = ionoscloud_dbaas_mongo.User(properties=ionoscloud_dbaas_mongo.UserProperties( + username=mongo_username, + password=mongo_password, + roles=user_roles, + )) - try: - mongo_cluster_id = get_resource_id( - module, ionoscloud_dbaas_mongo.ClustersApi(client).clusters_get(), - module.params.get('mongo_cluster'), - [['id'], ['properties', 'display_name']], - ) - user_response = users_api.clusters_users_post(mongo_cluster_id, mongo_user) - except ionoscloud_dbaas_mongo.ApiException as e: - module.fail_json(msg="failed to create the new Mongo Cluster User: %s" % to_native(e)) - return user_response - - -def _update_object(module, client, existing_object): - mongo_password = module.params.get('mongo_password') - user_roles = list(map( - lambda role: ionoscloud_dbaas_mongo.UserRoles(role=role['role'], database=role['database']), - module.params.get('user_roles'), - )) - users_api = ionoscloud_dbaas_mongo.UsersApi(client) - - mongo_user = ionoscloud_dbaas_mongo.PatchUserRequest(properties=ionoscloud_dbaas_mongo.PatchUserProperties( - password=mongo_password, - roles=user_roles, - )) - - try: - mongo_cluster_id = get_resource_id( - module, ionoscloud_dbaas_mongo.ClustersApi(client).clusters_get(), - module.params.get('mongo_cluster'), - [['id'], ['properties', 'display_name']], - ) - user_response = users_api.clusters_users_patch( - mongo_cluster_id, - module.params.get('mongo_username'), - mongo_user, - ) - except ionoscloud_dbaas_mongo.ApiException as e: - module.fail_json(msg="failed to update the Mongo Cluster User: %s" % to_native(e)) + try: + mongo_cluster_id = get_resource_id( + self.module, ionoscloud_dbaas_mongo.ClustersApi(client).clusters_get(), + self.module.params.get('mongo_cluster'), + [['id'], ['properties', 'display_name']], + ) + user_response = users_api.clusters_users_post(mongo_cluster_id, mongo_user) + except ionoscloud_dbaas_mongo.ApiException as e: + self.module.fail_json(msg="failed to create the new Mongo Cluster User: %s" % to_native(e)) + return user_response + + + def _update_object(self, existing_object, clients): + client = clients[0] + mongo_password = self.module.params.get('mongo_password') + user_roles = list(map( + lambda role: ionoscloud_dbaas_mongo.UserRoles(role=role['role'], database=role['database']), + self.module.params.get('user_roles'), + )) + users_api = ionoscloud_dbaas_mongo.UsersApi(client) + + mongo_user = ionoscloud_dbaas_mongo.PatchUserRequest(properties=ionoscloud_dbaas_mongo.PatchUserProperties( + password=mongo_password, + roles=user_roles, + )) + + try: + mongo_cluster_id = get_resource_id( + self.module, ionoscloud_dbaas_mongo.ClustersApi(client).clusters_get(), + self.module.params.get('mongo_cluster'), + [['id'], ['properties', 'display_name']], + ) + user_response = users_api.clusters_users_patch( + mongo_cluster_id, + self.module.params.get('mongo_username'), + mongo_user, + ) + except ionoscloud_dbaas_mongo.ApiException as e: + self.module.fail_json(msg="failed to update the Mongo Cluster User: %s" % to_native(e)) - return user_response + return user_response -def _remove_object(module, client, existing_object): - users_api = ionoscloud_dbaas_mongo.UsersApi(client) + def _remove_object(self, existing_object, clients): + client = clients[0] + users_api = ionoscloud_dbaas_mongo.UsersApi(client) - try: - mongo_cluster_id = get_resource_id( - module, ionoscloud_dbaas_mongo.ClustersApi(client).clusters_get(), - module.params.get('mongo_cluster'), - [['id'], ['properties', 'display_name']], - ) - users_api.clusters_users_delete(mongo_cluster_id, existing_object.properties.username) - if module.params.get('wait'): - try: - client.wait_for( - fn_request=lambda: users_api.clusters_users_find_by_id( - mongo_cluster_id, existing_object.properties.username, - ), - fn_check=lambda _: False, - scaleup=10000, - timeout=module.params.get('wait_timeout'), - ) - except ionoscloud_dbaas_mongo.ApiException as e: - if e.status != 404: - raise e - except Exception as e: - module.fail_json(msg="failed to delete the Mongo Cluster User: %s" % to_native(e)) - - -def update_replace_object(module, client, existing_object): - if _should_replace_object(module, existing_object): - - if not module.params.get('allow_replace'): - module.fail_json(msg="{} should be replaced but allow_replace is set to False.".format(OBJECT_NAME)) - - new_object = _create_object(module, client, existing_object).to_dict() - _remove_object(module, client, existing_object) - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: new_object, - } - if _should_update_object(module, existing_object): - # Update - return { - 'changed': True, - 'failed': False, - 'action': 'update', - RETURNED_KEY: _update_object(module, client, existing_object).to_dict() - } - - # No action - return { - 'changed': False, - 'failed': False, - 'action': 'create', - RETURNED_KEY: existing_object.to_dict() - } - - -def create_object(module, client): - existing_object = get_resource( - module, _get_object_list(module, client), _get_object_name(module), - [['properties', 'username']], - ) - - if existing_object: - return update_replace_object(module, client, existing_object) - - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: _create_object(module, client).to_dict() - } - - -def update_object(module, client): - object_list = _get_object_list(module, client) - - existing_object = get_resource( - module, object_list, _get_object_identifier(module), - [['properties', 'username']], - ) - - if existing_object is None: - module.exit_json(changed=False) - return - - return update_replace_object(module, client, existing_object) - - -def remove_object(module, client): - existing_object = get_resource( - module, _get_object_list(module, client), _get_object_identifier(module), - [['properties', 'username']], - ) - - if existing_object is None: - module.exit_json(changed=False) - return - - _remove_object(module, client, existing_object) - - return { - 'action': 'delete', - 'changed': True, - 'id': existing_object.properties.username, - } - - -def get_module_arguments(): - arguments = {} - - for option_name, option in OPTIONS.items(): - arguments[option_name] = { - 'type': option['type'], - } - for key in ['choices', 'default', 'aliases', 'no_log', 'elements']: - if option.get(key) is not None: - arguments[option_name][key] = option.get(key) - - if option.get('env_fallback'): - arguments[option_name]['fallback'] = (env_fallback, [option['env_fallback']]) - - if len(option.get('required', [])) == len(STATES): - arguments[option_name]['required'] = True - - return arguments - - -def get_sdk_config(module, sdk): - username = module.params.get('username') - password = module.params.get('password') - token = module.params.get('token') - api_url = module.params.get('api_url') - - if token is not None: - # use the token instead of username & password - conf = { - 'token': token - } - else: - # use the username & password - conf = { - 'username': username, - 'password': password, - } - - if api_url is not None: - conf['host'] = api_url - conf['server_index'] = None - - return sdk.Configuration(**conf) - - -def check_required_arguments(module, state, object_name): - # manually checking if token or username & password provided - if ( - not module.params.get("token") - and not (module.params.get("username") and module.params.get("password")) - ): - module.fail_json( - msg='Token or username & password are required for {object_name} state {state}'.format( - object_name=object_name, - state=state, - ), - ) - - for option_name, option in OPTIONS.items(): - if state in option.get('required', []) and not module.params.get(option_name): - module.fail_json( - msg='{option_name} parameter is required for {object_name} state {state}'.format( - option_name=option_name, - object_name=object_name, - state=state, - ), + try: + mongo_cluster_id = get_resource_id( + self.module, ionoscloud_dbaas_mongo.ClustersApi(client).clusters_get(), + self.module.params.get('mongo_cluster'), + [['id'], ['properties', 'display_name']], ) + users_api.clusters_users_delete(mongo_cluster_id, existing_object.properties.username) + if self.module.params.get('wait'): + try: + client.wait_for( + fn_request=lambda: users_api.clusters_users_find_by_id( + mongo_cluster_id, existing_object.properties.username, + ), + fn_check=lambda _: False, + scaleup=10000, + timeout=self.module.params.get('wait_timeout'), + ) + except ionoscloud_dbaas_mongo.ApiException as e: + if e.status != 404: + raise e + except Exception as e: + self.module.fail_json(msg="failed to delete the Mongo Cluster User: %s" % to_native(e)) -def main(): - module = AnsibleModule(argument_spec=get_module_arguments(), supports_check_mode=True) - +if __name__ == '__main__': + ionos_module = MongoUserModule() if not HAS_SDK: - module.fail_json(msg='both ionoscloud and ionoscloud_dbaas_mongo are required for this module, ' + ionos_module.module.fail_json(msg='both ionoscloud and ionoscloud_dbaas_mongo are required for this module, ' 'run `pip install ionoscloud ionoscloud_dbaas_mongo`') - - dbaas_mongo_api_client = ionoscloud_dbaas_mongo.ApiClient(get_sdk_config(module, ionoscloud_dbaas_mongo)) - dbaas_mongo_api_client.user_agent = DBAAS_MONGO_USER_AGENT - - state = module.params.get('state') - - check_required_arguments(module, state, OBJECT_NAME) - - try: - if state == 'present': - module.exit_json(**create_object(module, dbaas_mongo_api_client)) - elif state == 'update': - module.exit_json(**update_object(module, dbaas_mongo_api_client)) - elif state == 'absent': - module.exit_json(**remove_object(module, dbaas_mongo_api_client)) - except Exception as e: - module.fail_json( - msg='failed to set {object_name} state {state}: {error}'.format( - object_name=OBJECT_NAME, error=to_native(e), state=state, - ), - ) - - -if __name__ == '__main__': - main() + ionos_module.main() diff --git a/plugins/modules/nat_gateway.py b/plugins/modules/nat_gateway.py index c3ed4406..edeb9e9b 100644 --- a/plugins/modules/nat_gateway.py +++ b/plugins/modules/nat_gateway.py @@ -6,10 +6,6 @@ __metaclass__ = type -import re -import copy -import yaml - HAS_SDK = True try: @@ -17,14 +13,19 @@ from ionoscloud import __version__ as sdk_version from ionoscloud.models import NatGateway, NatGatewayProperties, NatGatewayLanProperties from ionoscloud.rest import ApiException - from ionoscloud import ApiClient except ImportError: HAS_SDK = False from ansible import __version__ -from ansible.module_utils.basic import AnsibleModule, env_fallback +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_module import CommonIonosModule +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_methods import ( + get_module_arguments, _get_request_id, get_resource_id, +) +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_options import get_default_options + ANSIBLE_METADATA = { 'metadata_version': '1.1', @@ -70,14 +71,8 @@ **get_default_options(STATES), } -def transform_for_documentation(val): - val['required'] = len(val.get('required', [])) == len(STATES) - del val['available'] - del val['type'] - return val -DOCUMENTATION = ''' ---- +DOCUMENTATION = """ module: nat_gateway short_description: Create or destroy a Ionos Cloud NATGateway. description: @@ -91,7 +86,7 @@ def transform_for_documentation(val): - "ionoscloud >= 6.0.2" author: - "IONOS Cloud SDK Team " -''' +""" EXAMPLE_PER_STATE = { 'present' : ''' @@ -136,367 +131,152 @@ def transform_for_documentation(val): ilowuerhfgwoqrghbqwoguh """ -uuid_match = re.compile('[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}', re.I) - - -def _get_matched_resources(resource_list, identity, identity_paths=None): - """ - Fetch and return a resource based on an identity supplied for it, if none or more than one matches - are found an error is printed and None is returned. - """ - - if identity_paths is None: - identity_paths = [['id'], ['properties', 'name']] - - def check_identity_method(resource): - resource_identity = [] - - for identity_path in identity_paths: - current = resource - for el in identity_path: - current = getattr(current, el) - resource_identity.append(current) - - return identity in resource_identity - - return list(filter(check_identity_method, resource_list.items)) - - -def get_resource(module, resource_list, identity, identity_paths=None): - matched_resources = _get_matched_resources(resource_list, identity, identity_paths) - - if len(matched_resources) == 1: - return matched_resources[0] - elif len(matched_resources) > 1: - module.fail_json(msg="found more resources of type {} for '{}'".format(resource_list.id, identity)) - else: - return None - - -def get_resource_id(module, resource_list, identity, identity_paths=None): - resource = get_resource(module, resource_list, identity, identity_paths) - return resource.id if resource is not None else None - - -def _get_request_id(headers): - match = re.search('/requests/([-A-Fa-f0-9]+)/', headers) - if match: - return match.group(1) - else: - raise Exception("Failed to extract request ID from response " - "header 'location': '{location}'".format(location=headers['location'])) - - -def _should_replace_object(module, existing_object): - return False - - -def _should_update_object(module, existing_object): - if module.params.get('lans'): - existing_lans = list(map( - lambda x: { 'gateway_ips': sorted(x.gateway_ips), 'id': str(x.id) }, - existing_object.properties.lans - )) - new_lans = list(map( - lambda x: { 'gateway_ips': sorted(x['gateway_ips']), 'id': x['id'] }, - module.params.get('lans') - )) - - return ( - module.params.get('name') is not None - and existing_object.properties.name != module.params.get('name') - or module.params.get('public_ips') is not None - and sorted(existing_object.properties.public_ips) != sorted(module.params.get('public_ips')) - or module.params.get('lans') is not None - and new_lans != existing_lans - ) - - -def _get_object_list(module, client): - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - return ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get(datacenter_id, depth=1) - - -def _get_object_name(module): - return module.params.get('name') - - -def _get_object_identifier(module): - return module.params.get('nat_gateway') - - -def _create_object(module, client, existing_object=None): - name = module.params.get('name') - public_ips = module.params.get('public_ips') - lans = module.params.get('lans') - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - if existing_object is not None: - name = existing_object.properties.name if name is None else name - public_ips = existing_object.properties.public_ips if public_ips is None else public_ips - lans = existing_object.properties.lans if lans is None else lans - - nat_gateways_api = ionoscloud.NATGatewaysApi(client) - - nat_gateway_lans = [] - if lans: - for lan in lans: - nat_gateway_lans.append(NatGatewayLanProperties(id=lan['id'], gateway_ips=lan['gateway_ips'])) - - nat_gateway_properties = NatGatewayProperties(name=name, public_ips=public_ips, lans=nat_gateway_lans) - nat_gateway = NatGateway(properties=nat_gateway_properties) - - try: - nat_gateway_response, _, headers = nat_gateways_api.datacenters_natgateways_post_with_http_info( - datacenter_id, nat_gateway, - ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - except ApiException as e: - module.fail_json(msg="failed to create the new NAT Gateway: %s" % to_native(e)) - return nat_gateway_response - - -def _update_object(module, client, existing_object): - name = module.params.get('name') - public_ips = module.params.get('public_ips') - lans = module.params.get('lans') - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - - nat_gateways_api = ionoscloud.NATGatewaysApi(client) - - nat_gateway_lans = [] - if lans: - for lan in lans: - nat_gateway_lans.append(NatGatewayLanProperties(id=lan['id'], gateway_ips=lan['gateway_ips'])) - nat_gateway_properties = NatGatewayProperties(name=name, public_ips=public_ips, lans=nat_gateway_lans) - - try: - nat_gateway_response, _, headers = nat_gateways_api.datacenters_natgateways_patch_with_http_info( - datacenter_id, existing_object.id, nat_gateway_properties, +class NatGatewayModule(CommonIonosModule): + def __init__(self) -> None: + super().__init__() + self.module = AnsibleModule(argument_spec=get_module_arguments(OPTIONS, STATES)) + self.returned_key = RETURNED_KEY + self.object_name = OBJECT_NAME + self.sdks = [ionoscloud] + self.user_agent = USER_AGENT + self.options = OPTIONS + + + def _should_replace_object(self, existing_object, clients): + return False + + + def _should_update_object(self, existing_object, clients): + if self.module.params.get('lans'): + existing_lans = list(map( + lambda x: { 'gateway_ips': sorted(x.gateway_ips), 'id': str(x.id) }, + existing_object.properties.lans + )) + new_lans = list(map( + lambda x: { 'gateway_ips': sorted(x['gateway_ips']), 'id': x['id'] }, + self.module.params.get('lans') + )) + + return ( + self.module.params.get('name') is not None + and existing_object.properties.name != self.module.params.get('name') + or self.module.params.get('public_ips') is not None + and sorted(existing_object.properties.public_ips) != sorted(self.module.params.get('public_ips')) + or self.module.params.get('lans') is not None + and new_lans != existing_lans ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - - return nat_gateway_response - except ApiException as e: - module.fail_json(msg="failed to update the NAT Gateway: %s" % to_native(e)) - -def _remove_object(module, client, existing_object): - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - nat_gateways_api = ionoscloud.NATGatewaysApi(client) - - try: - _, _, headers = nat_gateways_api.datacenters_natgateways_delete_with_http_info( - datacenter_id, existing_object.id, - ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - except ApiException as e: - module.fail_json(msg="failed to remove the NAT Gateway: %s" % to_native(e)) - - -def update_replace_object(module, client, existing_object): - if _should_replace_object(module, existing_object): - - if not module.params.get('allow_replace'): - module.fail_json(msg="{} should be replaced but allow_replace is set to False.".format(OBJECT_NAME)) - - new_object = _create_object(module, client, existing_object).to_dict() - _remove_object(module, client, existing_object) - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: new_object, - } - if _should_update_object(module, existing_object): - # Update - return { - 'changed': True, - 'failed': False, - 'action': 'update', - RETURNED_KEY: _update_object(module, client, existing_object).to_dict() - } - - # No action - return { - 'changed': False, - 'failed': False, - 'action': 'create', - RETURNED_KEY: existing_object.to_dict() - } - - -def create_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_name(module)) - - if existing_object: - return update_replace_object(module, client, existing_object) - - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: _create_object(module, client).to_dict() - } - - -def update_object(module, client): - object_name = _get_object_name(module) - object_list = _get_object_list(module, client) - - existing_object = get_resource(module, object_list, _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return - - existing_object_id_by_new_name = get_resource_id(module, object_list, object_name) - - if ( - existing_object.id is not None - and existing_object_id_by_new_name is not None - and existing_object_id_by_new_name != existing_object.id - ): - module.fail_json( - msg='failed to update the {}: Another resource with the desired name ({}) exists'.format( - OBJECT_NAME, object_name, - ), + def _get_object_list(self, clients): + client = clients[0] + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), ) + return ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get(datacenter_id, depth=1) - return update_replace_object(module, client, existing_object) - - -def remove_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return - - _remove_object(module, client, existing_object) - - return { - 'action': 'delete', - 'changed': True, - 'id': existing_object.id, - } + def _get_object_name(self): + return self.module.params.get('name') -def get_module_arguments(): - arguments = {} - for option_name, option in OPTIONS.items(): - arguments[option_name] = { - 'type': option['type'], - } - for key in ['choices', 'default', 'aliases', 'no_log', 'elements']: - if option.get(key) is not None: - arguments[option_name][key] = option.get(key) + def _get_object_identifier(self): + return self.module.params.get('nat_gateway') - if option.get('env_fallback'): - arguments[option_name]['fallback'] = (env_fallback, [option['env_fallback']]) - - if len(option.get('required', [])) == len(STATES): - arguments[option_name]['required'] = True - - return arguments + def _create_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + public_ips = self.module.params.get('public_ips') + lans = self.module.params.get('lans') + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + if existing_object is not None: + name = existing_object.properties.name if name is None else name + public_ips = existing_object.properties.public_ips if public_ips is None else public_ips + lans = existing_object.properties.lans if lans is None else lans -def get_sdk_config(module, sdk): - username = module.params.get('username') - password = module.params.get('password') - token = module.params.get('token') - api_url = module.params.get('api_url') - certificate_fingerprint = module.params.get('certificate_fingerprint') + nat_gateways_api = ionoscloud.NATGatewaysApi(client) + + nat_gateway_lans = [] + if lans: + for lan in lans: + nat_gateway_lans.append(NatGatewayLanProperties(id=lan['id'], gateway_ips=lan['gateway_ips'])) - if token is not None: - # use the token instead of username & password - conf = { - 'token': token - } - else: - # use the username & password - conf = { - 'username': username, - 'password': password, - } + nat_gateway_properties = NatGatewayProperties(name=name, public_ips=public_ips, lans=nat_gateway_lans) + nat_gateway = NatGateway(properties=nat_gateway_properties) - if api_url is not None: - conf['host'] = api_url - conf['server_index'] = None + try: + nat_gateway_response, _, headers = nat_gateways_api.datacenters_natgateways_post_with_http_info( + datacenter_id, nat_gateway, + ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) + except ApiException as e: + self.module.fail_json(msg="failed to create the new NAT Gateway: %s" % to_native(e)) + return nat_gateway_response - if certificate_fingerprint is not None: - conf['fingerprint'] = certificate_fingerprint - return sdk.Configuration(**conf) + def _update_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + public_ips = self.module.params.get('public_ips') + lans = self.module.params.get('lans') + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + nat_gateways_api = ionoscloud.NATGatewaysApi(client) -def check_required_arguments(module, state, object_name): - # manually checking if token or username & password provided - if ( - not module.params.get("token") - and not (module.params.get("username") and module.params.get("password")) - ): - module.fail_json( - msg='Token or username & password are required for {object_name} state {state}'.format( - object_name=object_name, - state=state, - ), - ) + nat_gateway_lans = [] + if lans: + for lan in lans: + nat_gateway_lans.append(NatGatewayLanProperties(id=lan['id'], gateway_ips=lan['gateway_ips'])) + nat_gateway_properties = NatGatewayProperties(name=name, public_ips=public_ips, lans=nat_gateway_lans) - for option_name, option in OPTIONS.items(): - if state in option.get('required', []) and not module.params.get(option_name): - module.fail_json( - msg='{option_name} parameter is required for {object_name} state {state}'.format( - option_name=option_name, - object_name=object_name, - state=state, - ), + try: + nat_gateway_response, _, headers = nat_gateways_api.datacenters_natgateways_patch_with_http_info( + datacenter_id, existing_object.id, nat_gateway_properties, ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) + return nat_gateway_response + except ApiException as e: + self.module.fail_json(msg="failed to update the NAT Gateway: %s" % to_native(e)) -def main(): - module = AnsibleModule(argument_spec=get_module_arguments(), supports_check_mode=True) - if not HAS_SDK: - module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') + def _remove_object(self, existing_object, clients): + client = clients[0] + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) - state = module.params.get('state') - with ApiClient(get_sdk_config(module, ionoscloud)) as api_client: - api_client.user_agent = USER_AGENT - check_required_arguments(module, state, OBJECT_NAME) + nat_gateways_api = ionoscloud.NATGatewaysApi(client) try: - if state == 'absent': - module.exit_json(**remove_object(module, api_client)) - elif state == 'present': - module.exit_json(**create_object(module, api_client)) - elif state == 'update': - module.exit_json(**update_object(module, api_client)) - except Exception as e: - module.fail_json(msg='failed to set {object_name} state {state}: {error}'.format(object_name=OBJECT_NAME, error=to_native(e), state=state)) + _, _, headers = nat_gateways_api.datacenters_natgateways_delete_with_http_info( + datacenter_id, existing_object.id, + ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) + except ApiException as e: + self.module.fail_json(msg="failed to remove the NAT Gateway: %s" % to_native(e)) + if __name__ == '__main__': - main() + ionos_module = NatGatewayModule() + if not HAS_SDK: + ionos_module.module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') + ionos_module.main() diff --git a/plugins/modules/nat_gateway_flowlog.py b/plugins/modules/nat_gateway_flowlog.py index 565b06f1..2c9005c0 100644 --- a/plugins/modules/nat_gateway_flowlog.py +++ b/plugins/modules/nat_gateway_flowlog.py @@ -6,10 +6,6 @@ __metaclass__ = type -import re -import copy -import yaml - HAS_SDK = True try: @@ -17,14 +13,20 @@ from ionoscloud import __version__ as sdk_version from ionoscloud.models import FlowLog, FlowLogProperties from ionoscloud.rest import ApiException - from ionoscloud import ApiClient except ImportError: HAS_SDK = False from ansible import __version__ -from ansible.module_utils.basic import AnsibleModule, env_fallback +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_module import CommonIonosModule +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_methods import ( + get_module_arguments, _get_request_id, get_resource_id, +) +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_options import get_default_options + + ANSIBLE_METADATA = { 'metadata_version': '1.1', 'status': ['preview'], @@ -82,14 +84,7 @@ **get_default_options(STATES), } -def transform_for_documentation(val): - val['required'] = len(val.get('required', [])) == len(STATES) - del val['available'] - del val['type'] - return val - -DOCUMENTATION = ''' ---- +DOCUMENTATION = """ module: nat_gateway_flowlog short_description: Create or destroy a Ionos Cloud NATGateway Flowlog. description: @@ -103,7 +98,7 @@ def transform_for_documentation(val): - "ionoscloud >= 6.0.2" author: - "IONOS Cloud SDK Team " -''' +""" EXAMPLE_PER_STATE = { 'present' : ''' @@ -147,388 +142,172 @@ def transform_for_documentation(val): """ -uuid_match = re.compile('[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}', re.I) - - -def _get_matched_resources(resource_list, identity, identity_paths=None): - """ - Fetch and return a resource based on an identity supplied for it, if none or more than one matches - are found an error is printed and None is returned. - """ - - if identity_paths is None: - identity_paths = [['id'], ['properties', 'name']] - - def check_identity_method(resource): - resource_identity = [] - - for identity_path in identity_paths: - current = resource - for el in identity_path: - current = getattr(current, el) - resource_identity.append(current) - - return identity in resource_identity - - return list(filter(check_identity_method, resource_list.items)) - - -def get_resource(module, resource_list, identity, identity_paths=None): - matched_resources = _get_matched_resources(resource_list, identity, identity_paths) - - if len(matched_resources) == 1: - return matched_resources[0] - elif len(matched_resources) > 1: - module.fail_json(msg="found more resources of type {} for '{}'".format(resource_list.id, identity)) - else: - return None - - -def get_resource_id(module, resource_list, identity, identity_paths=None): - resource = get_resource(module, resource_list, identity, identity_paths) - return resource.id if resource is not None else None - - -def _get_request_id(headers): - match = re.search('/requests/([-A-Fa-f0-9]+)/', headers) - if match: - return match.group(1) - else: - raise Exception("Failed to extract request ID from response " - "header 'location': '{location}'".format(location=headers['location'])) - - -def _should_replace_object(module, existing_object): - return False - - -def _should_update_object(module, existing_object): - return ( - module.params.get('name') is not None - and existing_object.properties.name != module.params.get('name') - or module.params.get('action') is not None - and existing_object.properties.action != module.params.get('action') - or module.params.get('direction') is not None - and existing_object.properties.direction != module.params.get('direction') - or module.params.get('bucket') is not None - and existing_object.properties.bucket != module.params.get('bucket') - ) - - -def _get_object_list(module, client): - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - nat_gateway_id = get_resource_id( - module, - ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( - datacenter_id, depth=1, - ), - module.params.get('nat_gateway'), - ) - - return ionoscloud.NATGatewaysApi(client).datacenters_natgateways_flowlogs_get( - datacenter_id, nat_gateway_id, depth=1, - ) - - -def _get_object_name(module): - return module.params.get('name') +class NatFlowlogModule(CommonIonosModule): + def __init__(self) -> None: + super().__init__() + self.module = AnsibleModule(argument_spec=get_module_arguments(OPTIONS, STATES)) + self.returned_key = RETURNED_KEY + self.object_name = OBJECT_NAME + self.sdks = [ionoscloud] + self.user_agent = USER_AGENT + self.options = OPTIONS -def _get_object_identifier(module): - return module.params.get('flowlog') + def _should_replace_object(self, existing_object, clients): + return False -def _create_object(module, client, existing_object=None): - name = module.params.get('name') - action = module.params.get('action') - direction = module.params.get('direction') - bucket = module.params.get('bucket') - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - nat_gateway_id = get_resource_id( - module, - ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( - datacenter_id, depth=1, - ), - module.params.get('nat_gateway'), - ) - - if existing_object is not None: - name = existing_object.properties.name if name is None else name - action = existing_object.properties.type if action is None else action - direction = existing_object.properties.direction if direction is None else direction - bucket = existing_object.properties.bucket if bucket is None else bucket - - nat_gateways_api = ionoscloud.NATGatewaysApi(client) - - nat_gateway_flowlog_properties = FlowLogProperties(name=name, action=action, direction=direction, bucket=bucket) - nat_gateway_flowlog = FlowLog(properties=nat_gateway_flowlog_properties) - - try: - response, _, headers = nat_gateways_api.datacenters_natgateways_flowlogs_post_with_http_info( - datacenter_id, nat_gateway_id, nat_gateway_flowlog, - ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=int(module.params.get('wait_timeout'))) - except ApiException as e: - module.fail_json(msg="failed to create the new NAT Gateway Flowlog: %s" % to_native(e)) - return response - - -def _update_object(module, client, existing_object): - name = module.params.get('name') - action = module.params.get('action') - direction = module.params.get('direction') - bucket = module.params.get('bucket') - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - nat_gateway_id = get_resource_id( - module, - ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( - datacenter_id, depth=1, - ), - module.params.get('nat_gateway'), - ) - - nat_gateways_api = ionoscloud.NATGatewaysApi(client) - - flowlog_properties = FlowLogProperties(name=name, action=action, direction=direction, bucket=bucket) - - try: - flowlog_response, _, headers = nat_gateways_api.datacenters_natgateways_flowlogs_patch_with_http_info( - datacenter_id, nat_gateway_id, existing_object.id, flowlog_properties, + def _should_update_object(self, existing_object, clients): + return ( + self.module.params.get('name') is not None + and existing_object.properties.name != self.module.params.get('name') + or self.module.params.get('action') is not None + and existing_object.properties.action != self.module.params.get('action') + or self.module.params.get('direction') is not None + and existing_object.properties.direction != self.module.params.get('direction') + or self.module.params.get('bucket') is not None + and existing_object.properties.bucket != self.module.params.get('bucket') ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - - return flowlog_response - except ApiException as e: - module.fail_json(msg="failed to update the NAT Gateway Flowlog: %s" % to_native(e)) - - -def _remove_object(module, client, existing_object): - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - nat_gateway_id = get_resource_id( - module, - ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( - datacenter_id, depth=1, - ), - module.params.get('nat_gateway'), - ) - - nat_gateways_api = ionoscloud.NATGatewaysApi(client) - - try: - _, _, headers = nat_gateways_api.datacenters_natgateways_flowlogs_delete_with_http_info( - datacenter_id, nat_gateway_id, existing_object.id, - ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - except ApiException as e: - module.fail_json(msg="failed to remove the NAT Gateway Flowlog: %s" % to_native(e)) - - -def update_replace_object(module, client, existing_object): - if _should_replace_object(module, existing_object): - - if not module.params.get('allow_replace'): - module.fail_json(msg="{} should be replaced but allow_replace is set to False.".format(OBJECT_NAME)) - - new_object = _create_object(module, client, existing_object).to_dict() - _remove_object(module, client, existing_object) - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: new_object, - } - if _should_update_object(module, existing_object): - # Update - return { - 'changed': True, - 'failed': False, - 'action': 'update', - RETURNED_KEY: _update_object(module, client, existing_object).to_dict() - } - - # No action - return { - 'changed': False, - 'failed': False, - 'action': 'create', - RETURNED_KEY: existing_object.to_dict() - } - - -def create_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_name(module)) - - if existing_object: - return update_replace_object(module, client, existing_object) - - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: _create_object(module, client).to_dict() - } - - -def update_object(module, client): - object_name = _get_object_name(module) - object_list = _get_object_list(module, client) - - existing_object = get_resource(module, object_list, _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return - - existing_object_id_by_new_name = get_resource_id(module, object_list, object_name) - - if ( - existing_object.id is not None - and existing_object_id_by_new_name is not None - and existing_object_id_by_new_name != existing_object.id - ): - module.fail_json( - msg='failed to update the {}: Another resource with the desired name ({}) exists'.format( - OBJECT_NAME, object_name, + def _get_object_list(self, clients): + client = clients[0] + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + nat_gateway_id = get_resource_id( + self.module, + ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( + datacenter_id, depth=1, ), + self.module.params.get('nat_gateway'), ) - return update_replace_object(module, client, existing_object) - - -def remove_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return + return ionoscloud.NATGatewaysApi(client).datacenters_natgateways_flowlogs_get( + datacenter_id, nat_gateway_id, depth=1, + ) - _remove_object(module, client, existing_object) - return { - 'action': 'delete', - 'changed': True, - 'id': existing_object.id, - } + def _get_object_name(self): + return self.module.params.get('name') -def get_module_arguments(): - arguments = {} + def _get_object_identifier(self): + return self.module.params.get('flowlog') - for option_name, option in OPTIONS.items(): - arguments[option_name] = { - 'type': option['type'], - } - for key in ['choices', 'default', 'aliases', 'no_log', 'elements']: - if option.get(key) is not None: - arguments[option_name][key] = option.get(key) - if option.get('env_fallback'): - arguments[option_name]['fallback'] = (env_fallback, [option['env_fallback']]) + def _create_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + action = self.module.params.get('action') + direction = self.module.params.get('direction') + bucket = self.module.params.get('bucket') + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + nat_gateway_id = get_resource_id( + self.module, + ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( + datacenter_id, depth=1, + ), + self.module.params.get('nat_gateway'), + ) - if len(option.get('required', [])) == len(STATES): - arguments[option_name]['required'] = True + if existing_object is not None: + name = existing_object.properties.name if name is None else name + action = existing_object.properties.type if action is None else action + direction = existing_object.properties.direction if direction is None else direction + bucket = existing_object.properties.bucket if bucket is None else bucket - return arguments + nat_gateways_api = ionoscloud.NATGatewaysApi(client) + + nat_gateway_flowlog_properties = FlowLogProperties(name=name, action=action, direction=direction, bucket=bucket) + nat_gateway_flowlog = FlowLog(properties=nat_gateway_flowlog_properties) + try: + response, _, headers = nat_gateways_api.datacenters_natgateways_flowlogs_post_with_http_info( + datacenter_id, nat_gateway_id, nat_gateway_flowlog, + ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=int(self.module.params.get('wait_timeout'))) + except ApiException as e: + self.module.fail_json(msg="failed to create the new NAT Gateway Flowlog: %s" % to_native(e)) + return response + + + def _update_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + action = self.module.params.get('action') + direction = self.module.params.get('direction') + bucket = self.module.params.get('bucket') + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + nat_gateway_id = get_resource_id( + self.module, + ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( + datacenter_id, depth=1, + ), + self.module.params.get('nat_gateway'), + ) -def get_sdk_config(module, sdk): - username = module.params.get('username') - password = module.params.get('password') - token = module.params.get('token') - api_url = module.params.get('api_url') - certificate_fingerprint = module.params.get('certificate_fingerprint') + nat_gateways_api = ionoscloud.NATGatewaysApi(client) - if token is not None: - # use the token instead of username & password - conf = { - 'token': token - } - else: - # use the username & password - conf = { - 'username': username, - 'password': password, - } + flowlog_properties = FlowLogProperties(name=name, action=action, direction=direction, bucket=bucket) - if api_url is not None: - conf['host'] = api_url - conf['server_index'] = None + try: + flowlog_response, _, headers = nat_gateways_api.datacenters_natgateways_flowlogs_patch_with_http_info( + datacenter_id, nat_gateway_id, existing_object.id, flowlog_properties, + ) - if certificate_fingerprint is not None: - conf['fingerprint'] = certificate_fingerprint + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) - return sdk.Configuration(**conf) + return flowlog_response + except ApiException as e: + self.module.fail_json(msg="failed to update the NAT Gateway Flowlog: %s" % to_native(e)) -def check_required_arguments(module, state, object_name): - # manually checking if token or username & password provided - if ( - not module.params.get("token") - and not (module.params.get("username") and module.params.get("password")) - ): - module.fail_json( - msg='Token or username & password are required for {object_name} state {state}'.format( - object_name=object_name, - state=state, + def _remove_object(self, existing_object, clients): + client = clients[0] + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + nat_gateway_id = get_resource_id( + self.module, + ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( + datacenter_id, depth=1, ), + self.module.params.get('nat_gateway'), ) - for option_name, option in OPTIONS.items(): - if state in option.get('required', []) and not module.params.get(option_name): - module.fail_json( - msg='{option_name} parameter is required for {object_name} state {state}'.format( - option_name=option_name, - object_name=object_name, - state=state, - ), - ) - - -def main(): - module = AnsibleModule(argument_spec=get_module_arguments(), supports_check_mode=True) - - if not HAS_SDK: - module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') - - state = module.params.get('state') - with ApiClient(get_sdk_config(module, ionoscloud)) as api_client: - api_client.user_agent = USER_AGENT - check_required_arguments(module, state, OBJECT_NAME) + nat_gateways_api = ionoscloud.NATGatewaysApi(client) try: - if state == 'absent': - module.exit_json(**remove_object(module, api_client)) - elif state == 'present': - module.exit_json(**create_object(module, api_client)) - elif state == 'update': - module.exit_json(**update_object(module, api_client)) - except Exception as e: - module.fail_json(msg='failed to set {object_name} state {state}: {error}'.format(object_name=OBJECT_NAME, error=to_native(e), state=state)) + _, _, headers = nat_gateways_api.datacenters_natgateways_flowlogs_delete_with_http_info( + datacenter_id, nat_gateway_id, existing_object.id, + ) + + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) + except ApiException as e: + self.module.fail_json(msg="failed to remove the NAT Gateway Flowlog: %s" % to_native(e)) if __name__ == '__main__': - main() + ionos_module = NatFlowlogModule() + if not HAS_SDK: + ionos_module.module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') + ionos_module.main() diff --git a/plugins/modules/nat_gateway_rule.py b/plugins/modules/nat_gateway_rule.py index 12128f55..90285078 100644 --- a/plugins/modules/nat_gateway_rule.py +++ b/plugins/modules/nat_gateway_rule.py @@ -6,10 +6,6 @@ __metaclass__ = type -import re -import copy -import yaml - HAS_SDK = True try: @@ -17,14 +13,19 @@ from ionoscloud import __version__ as sdk_version from ionoscloud.models import NatGatewayRule, NatGatewayRuleProperties from ionoscloud.rest import ApiException - from ionoscloud import ApiClient except ImportError: HAS_SDK = False from ansible import __version__ -from ansible.module_utils.basic import AnsibleModule, env_fallback +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_module import CommonIonosModule +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_methods import ( + get_module_arguments, _get_request_id, get_resource_id, +) +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_options import get_default_options + ANSIBLE_METADATA = { 'metadata_version': '1.1', @@ -98,14 +99,7 @@ **get_default_options(STATES), } -def transform_for_documentation(val): - val['required'] = len(val.get('required', [])) == len(STATES) - del val['available'] - del val['type'] - return val - -DOCUMENTATION = ''' ---- +DOCUMENTATION = """ module: nat_gateway_rule short_description: Create or destroy a Ionos Cloud NATGateway rule. description: @@ -119,7 +113,7 @@ def transform_for_documentation(val): - "ionoscloud >= 6.0.2" author: - "IONOS Cloud SDK Team " -''' +""" EXAMPLE_PER_STATE = { 'present' : ''' @@ -168,412 +162,197 @@ def transform_for_documentation(val): ilowuerhfgwoqrghbqwoguh """ -uuid_match = re.compile('[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}', re.I) - - -def _get_matched_resources(resource_list, identity, identity_paths=None): - """ - Fetch and return a resource based on an identity supplied for it, if none or more than one matches - are found an error is printed and None is returned. - """ - - if identity_paths is None: - identity_paths = [['id'], ['properties', 'name']] - - def check_identity_method(resource): - resource_identity = [] - - for identity_path in identity_paths: - current = resource - for el in identity_path: - current = getattr(current, el) - resource_identity.append(current) - - return identity in resource_identity - - return list(filter(check_identity_method, resource_list.items)) - - -def get_resource(module, resource_list, identity, identity_paths=None): - matched_resources = _get_matched_resources(resource_list, identity, identity_paths) - - if len(matched_resources) == 1: - return matched_resources[0] - elif len(matched_resources) > 1: - module.fail_json(msg="found more resources of type {} for '{}'".format(resource_list.id, identity)) - else: - return None - - -def get_resource_id(module, resource_list, identity, identity_paths=None): - resource = get_resource(module, resource_list, identity, identity_paths) - return resource.id if resource is not None else None - - -def _get_request_id(headers): - match = re.search('/requests/([-A-Fa-f0-9]+)/', headers) - if match: - return match.group(1) - else: - raise Exception("Failed to extract request ID from response " - "header 'location': '{location}'".format(location=headers['location'])) - - -def _should_replace_object(module, existing_object): - return False - - -def _should_update_object(module, existing_object): - return ( - module.params.get('name') is not None - and existing_object.properties.name != module.params.get('name') - or module.params.get('type') is not None - and existing_object.properties.type != module.params.get('type') - or module.params.get('protocol') is not None - and existing_object.properties.protocol != module.params.get('protocol') - or module.params.get('source_subnet') is not None - and existing_object.properties.source_subnet != module.params.get('source_subnet') - or module.params.get('public_ip') is not None - and existing_object.properties.public_ip != module.params.get('public_ip') - or module.params.get('target_subnet') is not None - and existing_object.properties.target_subnet != module.params.get('target_subnet') - or module.params.get('target_port_range') is not None - and existing_object.properties.target_port_range != module.params.get('target_port_range') - ) - - -def _get_object_list(module, client): - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - nat_gateway_id = get_resource_id( - module, - ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( - datacenter_id, depth=1, - ), - module.params.get('nat_gateway'), - ) - - return ionoscloud.NATGatewaysApi(client).datacenters_natgateways_rules_get( - datacenter_id, nat_gateway_id, depth=1, - ) - - -def _get_object_name(module): - return module.params.get('name') - - -def _get_object_identifier(module): - return module.params.get('nat_gateway_rule') - - -def _create_object(module, client, existing_object=None): - name = module.params.get('name') - nat_gateway_type = module.params.get('type') - protocol = module.params.get('protocol') - source_subnet = module.params.get('source_subnet') - public_ip = module.params.get('public_ip') - target_subnet = module.params.get('target_subnet') - target_port_range = module.params.get('target_port_range') - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - nat_gateway_id = get_resource_id( - module, - ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( - datacenter_id, depth=1, - ), - module.params.get('nat_gateway'), - ) - - if existing_object is not None: - name = existing_object.properties.name if name is None else name - nat_gateway_type = existing_object.properties.type if nat_gateway_type is None else nat_gateway_type - protocol = existing_object.properties.protocol if protocol is None else protocol - source_subnet = existing_object.properties.source_subnet if source_subnet is None else source_subnet - public_ip = existing_object.properties.public_ip if public_ip is None else public_ip - target_subnet = existing_object.properties.target_subnet if target_subnet is None else target_subnet - target_port_range = existing_object.properties.target_port_range if target_port_range is None else target_port_range - target_subnet = existing_object.properties.target_subnet if target_subnet is None else target_subnet - - nat_gateways_api = ionoscloud.NATGatewaysApi(client) - - nat_gateway_rule_properties = NatGatewayRuleProperties( - name=name, type=nat_gateway_type, protocol=protocol, - source_subnet=source_subnet, public_ip=public_ip, - target_subnet=target_subnet, - target_port_range=target_port_range, - ) - nat_gateway_rule = NatGatewayRule(properties=nat_gateway_rule_properties) - - try: - nat_gateway_rule_response, _, headers = nat_gateways_api.datacenters_natgateways_rules_post_with_http_info( - datacenter_id, nat_gateway_id, nat_gateway_rule, - ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=int(module.params.get('wait_timeout'))) - except ApiException as e: - module.fail_json(msg="failed to create the new NAT Gateway Rule: %s" % to_native(e)) - return nat_gateway_rule_response - - -def _update_object(module, client, existing_object): - name = module.params.get('name') - nat_gateway_type = module.params.get('type') - protocol = module.params.get('protocol') - source_subnet = module.params.get('source_subnet') - public_ip = module.params.get('public_ip') - target_subnet = module.params.get('target_subnet') - target_port_range = module.params.get('target_port_range') - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - nat_gateway_id = get_resource_id( - module, - ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( - datacenter_id, depth=1, - ), - module.params.get('nat_gateway'), - ) - - nat_gateways_api = ionoscloud.NATGatewaysApi(client) - - nat_gateway_rule_properties = NatGatewayRuleProperties( - name=name, type=nat_gateway_type, protocol=protocol, - source_subnet=source_subnet, public_ip=public_ip, - target_subnet=target_subnet, - target_port_range=target_port_range, - ) - - try: - response, _, headers = nat_gateways_api.datacenters_natgateways_rules_patch_with_http_info( - datacenter_id, nat_gateway_id, existing_object.id, nat_gateway_rule_properties, +class NatFlowlogModule(CommonIonosModule): + def __init__(self) -> None: + super().__init__() + self.module = AnsibleModule(argument_spec=get_module_arguments(OPTIONS, STATES)) + self.returned_key = RETURNED_KEY + self.object_name = OBJECT_NAME + self.sdks = [ionoscloud] + self.user_agent = USER_AGENT + self.options = OPTIONS + + + def _should_replace_object(self, existing_object, clients): + return False + + + def _should_update_object(self, existing_object, clients): + return ( + self.module.params.get('name') is not None + and existing_object.properties.name != self.module.params.get('name') + or self.module.params.get('type') is not None + and existing_object.properties.type != self.module.params.get('type') + or self.module.params.get('protocol') is not None + and existing_object.properties.protocol != self.module.params.get('protocol') + or self.module.params.get('source_subnet') is not None + and existing_object.properties.source_subnet != self.module.params.get('source_subnet') + or self.module.params.get('public_ip') is not None + and existing_object.properties.public_ip != self.module.params.get('public_ip') + or self.module.params.get('target_subnet') is not None + and existing_object.properties.target_subnet != self.module.params.get('target_subnet') + or self.module.params.get('target_port_range') is not None + and existing_object.properties.target_port_range != self.module.params.get('target_port_range') ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - - return response - except ApiException as e: - module.fail_json(msg="failed to update the NAT Gateway Rule: %s" % to_native(e)) - - -def _remove_object(module, client, existing_object): - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - nat_gateway_id = get_resource_id( - module, - ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( - datacenter_id, depth=1, - ), - module.params.get('nat_gateway'), - ) - - nat_gateways_api = ionoscloud.NATGatewaysApi(client) - - try: - _, _, headers = nat_gateways_api.datacenters_natgateways_rules_delete_with_http_info( - datacenter_id, nat_gateway_id, existing_object.id, + + def _get_object_list(self, clients): + client = clients[0] + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - except ApiException as e: - module.fail_json(msg="failed to remove the NAT Gateway Rule: %s" % to_native(e)) - - -def update_replace_object(module, client, existing_object): - if _should_replace_object(module, existing_object): - - if not module.params.get('allow_replace'): - module.fail_json(msg="{} should be replaced but allow_replace is set to False.".format(OBJECT_NAME)) - - new_object = _create_object(module, client, existing_object).to_dict() - _remove_object(module, client, existing_object) - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: new_object, - } - if _should_update_object(module, existing_object): - # Update - return { - 'changed': True, - 'failed': False, - 'action': 'update', - RETURNED_KEY: _update_object(module, client, existing_object).to_dict() - } - - # No action - return { - 'changed': False, - 'failed': False, - 'action': 'create', - RETURNED_KEY: existing_object.to_dict() - } - - -def create_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_name(module)) - - if existing_object: - return update_replace_object(module, client, existing_object) - - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: _create_object(module, client).to_dict() - } - - -def update_object(module, client): - object_name = _get_object_name(module) - object_list = _get_object_list(module, client) - - existing_object = get_resource(module, object_list, _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return - - existing_object_id_by_new_name = get_resource_id(module, object_list, object_name) - - if ( - existing_object.id is not None - and existing_object_id_by_new_name is not None - and existing_object_id_by_new_name != existing_object.id - ): - module.fail_json( - msg='failed to update the {}: Another resource with the desired name ({}) exists'.format( - OBJECT_NAME, object_name, + nat_gateway_id = get_resource_id( + self.module, + ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( + datacenter_id, depth=1, ), + self.module.params.get('nat_gateway'), ) - return update_replace_object(module, client, existing_object) - - -def remove_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return - - _remove_object(module, client, existing_object) + return ionoscloud.NATGatewaysApi(client).datacenters_natgateways_rules_get( + datacenter_id, nat_gateway_id, depth=1, + ) - return { - 'action': 'delete', - 'changed': True, - 'id': existing_object.id, - } + def _get_object_name(self): + return self.module.params.get('name') -def get_module_arguments(): - arguments = {} - for option_name, option in OPTIONS.items(): - arguments[option_name] = { - 'type': option['type'], - } - for key in ['choices', 'default', 'aliases', 'no_log', 'elements']: - if option.get(key) is not None: - arguments[option_name][key] = option.get(key) + def _get_object_identifier(self): + return self.module.params.get('nat_gateway_rule') - if option.get('env_fallback'): - arguments[option_name]['fallback'] = (env_fallback, [option['env_fallback']]) - if len(option.get('required', [])) == len(STATES): - arguments[option_name]['required'] = True + def _create_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + nat_gateway_type = self.module.params.get('type') + protocol = self.module.params.get('protocol') + source_subnet = self.module.params.get('source_subnet') + public_ip = self.module.params.get('public_ip') + target_subnet = self.module.params.get('target_subnet') + target_port_range = self.module.params.get('target_port_range') + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + nat_gateway_id = get_resource_id( + self.module, + ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( + datacenter_id, depth=1, + ), + self.module.params.get('nat_gateway'), + ) - return arguments + if existing_object is not None: + name = existing_object.properties.name if name is None else name + nat_gateway_type = existing_object.properties.type if nat_gateway_type is None else nat_gateway_type + protocol = existing_object.properties.protocol if protocol is None else protocol + source_subnet = existing_object.properties.source_subnet if source_subnet is None else source_subnet + public_ip = existing_object.properties.public_ip if public_ip is None else public_ip + target_subnet = existing_object.properties.target_subnet if target_subnet is None else target_subnet + target_port_range = existing_object.properties.target_port_range if target_port_range is None else target_port_range + target_subnet = existing_object.properties.target_subnet if target_subnet is None else target_subnet + + nat_gateways_api = ionoscloud.NATGatewaysApi(client) + + nat_gateway_rule_properties = NatGatewayRuleProperties( + name=name, type=nat_gateway_type, protocol=protocol, + source_subnet=source_subnet, public_ip=public_ip, + target_subnet=target_subnet, + target_port_range=target_port_range, + ) + nat_gateway_rule = NatGatewayRule(properties=nat_gateway_rule_properties) + try: + nat_gateway_rule_response, _, headers = nat_gateways_api.datacenters_natgateways_rules_post_with_http_info( + datacenter_id, nat_gateway_id, nat_gateway_rule, + ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=int(self.module.params.get('wait_timeout'))) + except ApiException as e: + self.module.fail_json(msg="failed to create the new NAT Gateway Rule: %s" % to_native(e)) + return nat_gateway_rule_response + + + def _update_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + nat_gateway_type = self.module.params.get('type') + protocol = self.module.params.get('protocol') + source_subnet = self.module.params.get('source_subnet') + public_ip = self.module.params.get('public_ip') + target_subnet = self.module.params.get('target_subnet') + target_port_range = self.module.params.get('target_port_range') + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + nat_gateway_id = get_resource_id( + self.module, + ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( + datacenter_id, depth=1, + ), + self.module.params.get('nat_gateway'), + ) -def get_sdk_config(module, sdk): - username = module.params.get('username') - password = module.params.get('password') - token = module.params.get('token') - api_url = module.params.get('api_url') - certificate_fingerprint = module.params.get('certificate_fingerprint') + nat_gateways_api = ionoscloud.NATGatewaysApi(client) - if token is not None: - # use the token instead of username & password - conf = { - 'token': token - } - else: - # use the username & password - conf = { - 'username': username, - 'password': password, - } + nat_gateway_rule_properties = NatGatewayRuleProperties( + name=name, type=nat_gateway_type, protocol=protocol, + source_subnet=source_subnet, public_ip=public_ip, + target_subnet=target_subnet, + target_port_range=target_port_range, + ) - if api_url is not None: - conf['host'] = api_url - conf['server_index'] = None + try: + response, _, headers = nat_gateways_api.datacenters_natgateways_rules_patch_with_http_info( + datacenter_id, nat_gateway_id, existing_object.id, nat_gateway_rule_properties, + ) - if certificate_fingerprint is not None: - conf['fingerprint'] = certificate_fingerprint + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) - return sdk.Configuration(**conf) + return response + except ApiException as e: + self.module.fail_json(msg="failed to update the NAT Gateway Rule: %s" % to_native(e)) -def check_required_arguments(module, state, object_name): - # manually checking if token or username & password provided - if ( - not module.params.get("token") - and not (module.params.get("username") and module.params.get("password")) - ): - module.fail_json( - msg='Token or username & password are required for {object_name} state {state}'.format( - object_name=object_name, - state=state, + def _remove_object(self, existing_object, clients): + client = clients[0] + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + nat_gateway_id = get_resource_id( + self.module, + ionoscloud.NATGatewaysApi(client).datacenters_natgateways_get( + datacenter_id, depth=1, ), + self.module.params.get('nat_gateway'), ) - for option_name, option in OPTIONS.items(): - if state in option.get('required', []) and not module.params.get(option_name): - module.fail_json( - msg='{option_name} parameter is required for {object_name} state {state}'.format( - option_name=option_name, - object_name=object_name, - state=state, - ), - ) - - -def main(): - module = AnsibleModule(argument_spec=get_module_arguments(), supports_check_mode=True) - - if not HAS_SDK: - module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') - - state = module.params.get('state') - with ApiClient(get_sdk_config(module, ionoscloud)) as api_client: - api_client.user_agent = USER_AGENT - check_required_arguments(module, state, OBJECT_NAME) + nat_gateways_api = ionoscloud.NATGatewaysApi(client) try: - if state == 'present': - module.exit_json(**create_object(module, api_client)) - elif state == 'update': - module.exit_json(**update_object(module, api_client)) - elif state == 'absent': - module.exit_json(**remove_object(module, api_client)) - except Exception as e: - module.fail_json(msg='failed to set {object_name} state {state}: {error}'.format(object_name=OBJECT_NAME, error=to_native(e), state=state)) + _, _, headers = nat_gateways_api.datacenters_natgateways_rules_delete_with_http_info( + datacenter_id, nat_gateway_id, existing_object.id, + ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) + except ApiException as e: + self.module.fail_json(msg="failed to remove the NAT Gateway Rule: %s" % to_native(e)) + if __name__ == '__main__': - main() + ionos_module = NatFlowlogModule() + if not HAS_SDK: + ionos_module.module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') + ionos_module.main() diff --git a/plugins/modules/network_load_balancer.py b/plugins/modules/network_load_balancer.py index 4652a017..6bbf15e2 100644 --- a/plugins/modules/network_load_balancer.py +++ b/plugins/modules/network_load_balancer.py @@ -6,10 +6,6 @@ __metaclass__ = type -import re -import copy -import yaml - HAS_SDK = True try: @@ -17,14 +13,20 @@ from ionoscloud import __version__ as sdk_version from ionoscloud.models import NetworkLoadBalancer, NetworkLoadBalancerProperties from ionoscloud.rest import ApiException - from ionoscloud import ApiClient except ImportError: HAS_SDK = False from ansible import __version__ -from ansible.module_utils.basic import AnsibleModule, env_fallback +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_module import CommonIonosModule +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_methods import ( + get_module_arguments, _get_request_id, get_resource_id, +) +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_options import get_default_options + + ANSIBLE_METADATA = { 'metadata_version': '1.1', 'status': ['preview'], @@ -80,14 +82,7 @@ **get_default_options(STATES), } -def transform_for_documentation(val): - val['required'] = len(val.get('required', [])) == len(STATES) - del val['available'] - del val['type'] - return val - -DOCUMENTATION = ''' ---- +DOCUMENTATION = """ module: network_load_balancer short_description: Create or destroy a Ionos Cloud NetworkLoadbalancer. description: @@ -101,7 +96,7 @@ def transform_for_documentation(val): - "ionoscloud >= 6.0.2" author: - "IONOS Cloud SDK Team " -''' +""" EXAMPLE_PER_STATE = { 'present' : ''' @@ -142,365 +137,150 @@ def transform_for_documentation(val): ilowuerhfgwoqrghbqwoguh """ -uuid_match = re.compile('[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}', re.I) - - -def _get_matched_resources(resource_list, identity, identity_paths=None): - """ - Fetch and return a resource based on an identity supplied for it, if none or more than one matches - are found an error is printed and None is returned. - """ - - if identity_paths is None: - identity_paths = [['id'], ['properties', 'name']] - - def check_identity_method(resource): - resource_identity = [] - - for identity_path in identity_paths: - current = resource - for el in identity_path: - current = getattr(current, el) - resource_identity.append(current) - - return identity in resource_identity - - return list(filter(check_identity_method, resource_list.items)) - - -def get_resource(module, resource_list, identity, identity_paths=None): - matched_resources = _get_matched_resources(resource_list, identity, identity_paths) - - if len(matched_resources) == 1: - return matched_resources[0] - elif len(matched_resources) > 1: - module.fail_json(msg="found more resources of type {} for '{}'".format(resource_list.id, identity)) - else: - return None - - -def get_resource_id(module, resource_list, identity, identity_paths=None): - resource = get_resource(module, resource_list, identity, identity_paths) - return resource.id if resource is not None else None - - -def _get_request_id(headers): - match = re.search('/requests/([-A-Fa-f0-9]+)/', headers) - if match: - return match.group(1) - else: - raise Exception("Failed to extract request ID from response " - "header 'location': '{location}'".format(location=headers['location'])) - - -def _should_replace_object(module, existing_object): - return False - +class NetworkLoadBalancerFlowlogModule(CommonIonosModule): + def __init__(self) -> None: + super().__init__() + self.module = AnsibleModule(argument_spec=get_module_arguments(OPTIONS, STATES)) + self.returned_key = RETURNED_KEY + self.object_name = OBJECT_NAME + self.sdks = [ionoscloud] + self.user_agent = USER_AGENT + self.options = OPTIONS + + + def _should_replace_object(self, existing_object, clients): + return False + + + def _should_update_object(self, existing_object, clients): + return ( + self.module.params.get('name') is not None + and existing_object.properties.name != self.module.params.get('name') + or self.module.params.get('ips') is not None + and sorted(existing_object.properties.ips) != sorted(self.module.params.get('ips')) + or self.module.params.get('lb_private_ips') is not None + and sorted(existing_object.properties.lb_private_ips) != sorted(self.module.params.get('lb_private_ips')) + or self.module.params.get('listener_lan') is not None + and existing_object.properties.listener_lan != self.module.params.get('listener_lan') + or self.module.params.get('target_lan') is not None + and existing_object.properties.target_lan != self.module.params.get('target_lan') + ) -def _should_update_object(module, existing_object): - return ( - module.params.get('name') is not None - and existing_object.properties.name != module.params.get('name') - or module.params.get('ips') is not None - and sorted(existing_object.properties.ips) != sorted(module.params.get('ips')) - or module.params.get('lb_private_ips') is not None - and sorted(existing_object.properties.lb_private_ips) != sorted(module.params.get('lb_private_ips')) - or module.params.get('listener_lan') is not None - and existing_object.properties.listener_lan != module.params.get('listener_lan') - or module.params.get('target_lan') is not None - and existing_object.properties.target_lan != module.params.get('target_lan') - ) + def _get_object_list(self, clients): + client = clients[0] + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + return ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get(datacenter_id, depth=1) -def _get_object_list(module, client): - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - return ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get(datacenter_id, depth=1) -def _get_object_name(module): - return module.params.get('name') + def _get_object_name(self): + return self.module.params.get('name') -def _get_object_identifier(module): - return module.params.get('network_load_balancer') + def _get_object_identifier(self): + return self.module.params.get('network_load_balancer') -def _create_object(module, client, existing_object=None): - name = module.params.get('name') - ips = module.params.get('ips') - listener_lan = module.params.get('listener_lan') - target_lan = module.params.get('target_lan') - lb_private_ips = module.params.get('lb_private_ips') - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - if existing_object is not None: - name = existing_object.properties.name if name is None else name - ips = existing_object.properties.ips if ips is None else ips - listener_lan = existing_object.properties.listener_lan if listener_lan is None else listener_lan - target_lan = existing_object.properties.target_lan if target_lan is None else target_lan - lb_private_ips = existing_object.properties.lb_private_ips if lb_private_ips is None else lb_private_ips - - network_loadbalancers_api = ionoscloud.NetworkLoadBalancersApi(client) - - nlb_properties = NetworkLoadBalancerProperties( - name=name, listener_lan=listener_lan, ips=ips, - target_lan=target_lan, lb_private_ips=lb_private_ips, - ) - network_load_balancer = NetworkLoadBalancer(properties=nlb_properties) - - try: - response, _, headers = network_loadbalancers_api.datacenters_networkloadbalancers_post_with_http_info( - datacenter_id, network_load_balancer, + def _create_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + ips = self.module.params.get('ips') + listener_lan = self.module.params.get('listener_lan') + target_lan = self.module.params.get('target_lan') + lb_private_ips = self.module.params.get('lb_private_ips') + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - except ApiException as e: - module.fail_json(msg="failed to create the new Network Loadbalancer: %s" % to_native(e)) - return response - - -def _update_object(module, client, existing_object): - name = module.params.get('name') - ips = module.params.get('ips') - listener_lan = module.params.get('listener_lan') - target_lan = module.params.get('target_lan') - lb_private_ips = module.params.get('lb_private_ips') - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - - nlbs_api = ionoscloud.NetworkLoadBalancersApi(client) - - nlb_properties = NetworkLoadBalancerProperties( - name=name, listener_lan=listener_lan, ips=ips, - target_lan=target_lan, lb_private_ips=lb_private_ips, - ) - - try: - response, _, headers = nlbs_api.datacenters_networkloadbalancers_patch_with_http_info( - datacenter_id, existing_object.id, nlb_properties, + if existing_object is not None: + name = existing_object.properties.name if name is None else name + ips = existing_object.properties.ips if ips is None else ips + listener_lan = existing_object.properties.listener_lan if listener_lan is None else listener_lan + target_lan = existing_object.properties.target_lan if target_lan is None else target_lan + lb_private_ips = existing_object.properties.lb_private_ips if lb_private_ips is None else lb_private_ips + + network_loadbalancers_api = ionoscloud.NetworkLoadBalancersApi(client) + + nlb_properties = NetworkLoadBalancerProperties( + name=name, listener_lan=listener_lan, ips=ips, + target_lan=target_lan, lb_private_ips=lb_private_ips, ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) + network_load_balancer = NetworkLoadBalancer(properties=nlb_properties) + try: + response, _, headers = network_loadbalancers_api.datacenters_networkloadbalancers_post_with_http_info( + datacenter_id, network_load_balancer, + ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) + except ApiException as e: + self.module.fail_json(msg="failed to create the new Network Loadbalancer: %s" % to_native(e)) return response - except ApiException as e: - module.fail_json(msg="failed to update the Network Loadbalancer: %s" % to_native(e)) - -def _remove_object(module, client, existing_object): - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - nlbs_api = ionoscloud.NetworkLoadBalancersApi(client) - - try: - _, _, headers = nlbs_api.datacenters_networkloadbalancers_delete_with_http_info( - datacenter_id, existing_object.id, - ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - except ApiException as e: - module.fail_json(msg="failed to remove the Network Loadbalancer: %s" % to_native(e)) - - -def update_replace_object(module, client, existing_object): - if _should_replace_object(module, existing_object): - - if not module.params.get('allow_replace'): - module.fail_json(msg="{} should be replaced but allow_replace is set to False.".format(OBJECT_NAME)) - - new_object = _create_object(module, client, existing_object).to_dict() - _remove_object(module, client, existing_object) - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: new_object, - } - if _should_update_object(module, existing_object): - # Update - return { - 'changed': True, - 'failed': False, - 'action': 'update', - RETURNED_KEY: _update_object(module, client, existing_object).to_dict() - } - - # No action - return { - 'changed': False, - 'failed': False, - 'action': 'create', - RETURNED_KEY: existing_object.to_dict() - } - - -def create_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_name(module)) - - if existing_object: - return update_replace_object(module, client, existing_object) - - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: _create_object(module, client).to_dict() - } - - -def update_object(module, client): - object_name = _get_object_name(module) - object_list = _get_object_list(module, client) - - existing_object = get_resource(module, object_list, _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return - - existing_object_id_by_new_name = get_resource_id(module, object_list, object_name) - - if ( - existing_object.id is not None - and existing_object_id_by_new_name is not None - and existing_object_id_by_new_name != existing_object.id - ): - module.fail_json( - msg='failed to update the {}: Another resource with the desired name ({}) exists'.format( - OBJECT_NAME, object_name, - ), + def _update_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + ips = self.module.params.get('ips') + listener_lan = self.module.params.get('listener_lan') + target_lan = self.module.params.get('target_lan') + lb_private_ips = self.module.params.get('lb_private_ips') + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), ) - return update_replace_object(module, client, existing_object) - - -def remove_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return - - _remove_object(module, client, existing_object) - - return { - 'action': 'delete', - 'changed': True, - 'id': existing_object.id, - } - - -def get_module_arguments(): - arguments = {} - - for option_name, option in OPTIONS.items(): - arguments[option_name] = { - 'type': option['type'], - } - for key in ['choices', 'default', 'aliases', 'no_log', 'elements']: - if option.get(key) is not None: - arguments[option_name][key] = option.get(key) - - if option.get('env_fallback'): - arguments[option_name]['fallback'] = (env_fallback, [option['env_fallback']]) - - if len(option.get('required', [])) == len(STATES): - arguments[option_name]['required'] = True - - return arguments - - -def get_sdk_config(module, sdk): - username = module.params.get('username') - password = module.params.get('password') - token = module.params.get('token') - api_url = module.params.get('api_url') - certificate_fingerprint = module.params.get('certificate_fingerprint') - - if token is not None: - # use the token instead of username & password - conf = { - 'token': token - } - else: - # use the username & password - conf = { - 'username': username, - 'password': password, - } - - if api_url is not None: - conf['host'] = api_url - conf['server_index'] = None - - if certificate_fingerprint is not None: - conf['fingerprint'] = certificate_fingerprint - - return sdk.Configuration(**conf) - - -def check_required_arguments(module, state, object_name): - # manually checking if token or username & password provided - if ( - not module.params.get("token") - and not (module.params.get("username") and module.params.get("password")) - ): - module.fail_json( - msg='Token or username & password are required for {object_name} state {state}'.format( - object_name=object_name, - state=state, - ), + nlbs_api = ionoscloud.NetworkLoadBalancersApi(client) + + nlb_properties = NetworkLoadBalancerProperties( + name=name, listener_lan=listener_lan, ips=ips, + target_lan=target_lan, lb_private_ips=lb_private_ips, ) - for option_name, option in OPTIONS.items(): - if state in option.get('required', []) and not module.params.get(option_name): - module.fail_json( - msg='{option_name} parameter is required for {object_name} state {state}'.format( - option_name=option_name, - object_name=object_name, - state=state, - ), + try: + response, _, headers = nlbs_api.datacenters_networkloadbalancers_patch_with_http_info( + datacenter_id, existing_object.id, nlb_properties, ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) + return response + except ApiException as e: + self.module.fail_json(msg="failed to update the Network Loadbalancer: %s" % to_native(e)) -def main(): - module = AnsibleModule(argument_spec=get_module_arguments(), supports_check_mode=True) - if not HAS_SDK: - module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') + def _remove_object(self, existing_object, clients): + client = clients[0] + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) - state = module.params.get('state') - with ApiClient(get_sdk_config(module, ionoscloud)) as api_client: - api_client.user_agent = USER_AGENT - check_required_arguments(module, state, OBJECT_NAME) + nlbs_api = ionoscloud.NetworkLoadBalancersApi(client) try: - if state == 'absent': - module.exit_json(**remove_object(module, api_client)) - elif state == 'present': - module.exit_json(**create_object(module, api_client)) - elif state == 'update': - module.exit_json(**update_object(module, api_client)) - except Exception as e: - module.fail_json(msg='failed to set {object_name} state {state}: {error}'.format(object_name=OBJECT_NAME, error=to_native(e), state=state)) + _, _, headers = nlbs_api.datacenters_networkloadbalancers_delete_with_http_info( + datacenter_id, existing_object.id, + ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) + except ApiException as e: + self.module.fail_json(msg="failed to remove the Network Loadbalancer: %s" % to_native(e)) if __name__ == '__main__': - main() + ionos_module = NetworkLoadBalancerFlowlogModule() + if not HAS_SDK: + ionos_module.module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') + ionos_module.main() diff --git a/plugins/modules/network_load_balancer_flowlog.py b/plugins/modules/network_load_balancer_flowlog.py index 2a3e7a35..52b565e0 100644 --- a/plugins/modules/network_load_balancer_flowlog.py +++ b/plugins/modules/network_load_balancer_flowlog.py @@ -6,25 +6,27 @@ __metaclass__ = type -import re -import copy -import yaml - HAS_SDK = True try: import ionoscloud from ionoscloud import __version__ as sdk_version - from ionoscloud.models import NetworkLoadBalancer, NetworkLoadBalancerProperties, FlowLog, FlowLogProperties + from ionoscloud.models import FlowLog, FlowLogProperties from ionoscloud.rest import ApiException - from ionoscloud import ApiClient except ImportError: HAS_SDK = False from ansible import __version__ -from ansible.module_utils.basic import AnsibleModule, env_fallback +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_module import CommonIonosModule +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_methods import ( + get_module_arguments, _get_request_id, get_resource_id, +) +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_options import get_default_options + + ANSIBLE_METADATA = { 'metadata_version': '1.1', 'status': ['preview'], @@ -82,14 +84,7 @@ **get_default_options(STATES), } -def transform_for_documentation(val): - val['required'] = len(val.get('required', [])) == len(STATES) - del val['available'] - del val['type'] - return val - -DOCUMENTATION = ''' ---- +DOCUMENTATION = """ module: network_load_balancer_flowlog short_description: Create or destroy a Ionos Cloud NetworkLoadbalancer Flowlog. description: @@ -103,7 +98,7 @@ def transform_for_documentation(val): - "ionoscloud >= 6.0.2" author: - "IONOS Cloud SDK Team " -''' +""" EXAMPLE_PER_STATE = { 'present' : ''' @@ -146,388 +141,173 @@ def transform_for_documentation(val): ilowuerhfgwoqrghbqwoguh """ -uuid_match = re.compile('[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}', re.I) +class NetworkLoadBalancerFlowlogModule(CommonIonosModule): + def __init__(self) -> None: + super().__init__() + self.module = AnsibleModule(argument_spec=get_module_arguments(OPTIONS, STATES)) + self.returned_key = RETURNED_KEY + self.object_name = OBJECT_NAME + self.sdks = [ionoscloud] + self.user_agent = USER_AGENT + self.options = OPTIONS -def _get_matched_resources(resource_list, identity, identity_paths=None): - """ - Fetch and return a resource based on an identity supplied for it, if none or more than one matches - are found an error is printed and None is returned. - """ - if identity_paths is None: - identity_paths = [['id'], ['properties', 'name']] + def _should_replace_object(self, existing_object, clients): + return False - def check_identity_method(resource): - resource_identity = [] - for identity_path in identity_paths: - current = resource - for el in identity_path: - current = getattr(current, el) - resource_identity.append(current) - - return identity in resource_identity - - return list(filter(check_identity_method, resource_list.items)) - - -def get_resource(module, resource_list, identity, identity_paths=None): - matched_resources = _get_matched_resources(resource_list, identity, identity_paths) - - if len(matched_resources) == 1: - return matched_resources[0] - elif len(matched_resources) > 1: - module.fail_json(msg="found more resources of type {} for '{}'".format(resource_list.id, identity)) - else: - return None - - -def get_resource_id(module, resource_list, identity, identity_paths=None): - resource = get_resource(module, resource_list, identity, identity_paths) - return resource.id if resource is not None else None - - -def _get_request_id(headers): - match = re.search('/requests/([-A-Fa-f0-9]+)/', headers) - if match: - return match.group(1) - else: - raise Exception("Failed to extract request ID from response " - "header 'location': '{location}'".format(location=headers['location'])) - - -def _should_replace_object(module, existing_object): - return False - - -def _should_update_object(module, existing_object): - return ( - module.params.get('name') is not None - and existing_object.properties.name != module.params.get('name') - or module.params.get('action') is not None - and existing_object.properties.action != module.params.get('action') - or module.params.get('direction') is not None - and existing_object.properties.direction != module.params.get('direction') - or module.params.get('bucket') is not None - and existing_object.properties.bucket != module.params.get('bucket') - ) - - -def _get_object_list(module, client): - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - network_load_balancer_id = get_resource_id( - module, - ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( - datacenter_id, depth=1, - ), - module.params.get('network_load_balancer'), - ) - - return ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_flowlogs_get( - datacenter_id, network_load_balancer_id, depth=1, - ) - - -def _get_object_name(module): - return module.params.get('name') - - -def _get_object_identifier(module): - return module.params.get('flowlog') - - -def _create_object(module, client, existing_object=None): - name = module.params.get('name') - action = module.params.get('action') - direction = module.params.get('direction') - bucket = module.params.get('bucket') - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - network_load_balancer_id = get_resource_id( - module, - ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( - datacenter_id, depth=1, - ), - module.params.get('network_load_balancer'), - ) - - if existing_object is not None: - name = existing_object.properties.name if name is None else name - action = existing_object.properties.type if action is None else action - direction = existing_object.properties.direction if direction is None else direction - bucket = existing_object.properties.bucket if bucket is None else bucket - - network_loadbalancers_api = ionoscloud.NetworkLoadBalancersApi(client) - - nlb_flowlog_properties = FlowLogProperties(name=name, action=action, direction=direction, bucket=bucket) - nlb_flowlog = FlowLog(properties=nlb_flowlog_properties) - - try: - response, _, headers = network_loadbalancers_api.datacenters_networkloadbalancers_flowlogs_post_with_http_info( - datacenter_id, network_load_balancer_id, nlb_flowlog, - ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=int(module.params.get('wait_timeout'))) - except ApiException as e: - module.fail_json(msg="failed to create the new Network Loadbalancer Flowlog: %s" % to_native(e)) - return response - - -def _update_object(module, client, existing_object): - name = module.params.get('name') - action = module.params.get('action') - direction = module.params.get('direction') - bucket = module.params.get('bucket') - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - network_load_balancer_id = get_resource_id( - module, - ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( - datacenter_id, depth=1, - ), - module.params.get('network_load_balancer'), - ) - - network_loadbalancers_api = ionoscloud.NetworkLoadBalancersApi(client) - - flowlog_properties = FlowLogProperties(name=name, action=action, direction=direction, bucket=bucket) - - try: - response, _, headers = network_loadbalancers_api.datacenters_networkloadbalancers_flowlogs_patch_with_http_info( - datacenter_id, network_load_balancer_id, existing_object.id, flowlog_properties, + def _should_update_object(self, existing_object, clients): + return ( + self.module.params.get('name') is not None + and existing_object.properties.name != self.module.params.get('name') + or self.module.params.get('action') is not None + and existing_object.properties.action != self.module.params.get('action') + or self.module.params.get('direction') is not None + and existing_object.properties.direction != self.module.params.get('direction') + or self.module.params.get('bucket') is not None + and existing_object.properties.bucket != self.module.params.get('bucket') ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=int(module.params.get('wait_timeout'))) - return response - except ApiException as e: - module.fail_json(msg="failed to update the Network Loadbalancer Flowlog: %s" % to_native(e)) - - -def _remove_object(module, client, existing_object): - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - network_load_balancer_id = get_resource_id( - module, - ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( - datacenter_id, depth=1, - ), - module.params.get('network_load_balancer'), - ) - - network_loadbalancers_api = ionoscloud.NetworkLoadBalancersApi(client) - - try: - _, _, headers = network_loadbalancers_api.datacenters_networkloadbalancers_flowlogs_delete_with_http_info( - datacenter_id, network_load_balancer_id, existing_object.id, + def _get_object_list(self, clients): + client = clients[0] + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), ) - - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=int(module.params.get('wait_timeout'))) - except ApiException as e: - module.fail_json(msg="failed to remove the Network Loadbalancer Flowlog: %s" % to_native(e)) - - -def update_replace_object(module, client, existing_object): - if _should_replace_object(module, existing_object): - - if not module.params.get('allow_replace'): - module.fail_json(msg="{} should be replaced but allow_replace is set to False.".format(OBJECT_NAME)) - - new_object = _create_object(module, client, existing_object).to_dict() - _remove_object(module, client, existing_object) - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: new_object, - } - if _should_update_object(module, existing_object): - # Update - return { - 'changed': True, - 'failed': False, - 'action': 'update', - RETURNED_KEY: _update_object(module, client, existing_object).to_dict() - } - - # No action - return { - 'changed': False, - 'failed': False, - 'action': 'create', - RETURNED_KEY: existing_object.to_dict() - } - - -def create_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_name(module)) - - if existing_object: - return update_replace_object(module, client, existing_object) - - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: _create_object(module, client).to_dict() - } - - -def update_object(module, client): - object_name = _get_object_name(module) - object_list = _get_object_list(module, client) - - existing_object = get_resource(module, object_list, _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return - - existing_object_id_by_new_name = get_resource_id(module, object_list, object_name) - - if ( - existing_object.id is not None - and existing_object_id_by_new_name is not None - and existing_object_id_by_new_name != existing_object.id - ): - module.fail_json( - msg='failed to update the {}: Another resource with the desired name ({}) exists'.format( - OBJECT_NAME, object_name, + network_load_balancer_id = get_resource_id( + self.module, + ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( + datacenter_id, depth=1, ), + self.module.params.get('network_load_balancer'), ) - return update_replace_object(module, client, existing_object) - + return ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_flowlogs_get( + datacenter_id, network_load_balancer_id, depth=1, + ) -def remove_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_identifier(module)) - if existing_object is None: - module.exit_json(changed=False) - return + def _get_object_name(self): + return self.module.params.get('name') - _remove_object(module, client, existing_object) - return { - 'action': 'delete', - 'changed': True, - 'id': existing_object.id, - } + def _get_object_identifier(self): + return self.module.params.get('flowlog') -def get_module_arguments(): - arguments = {} + def _create_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + action = self.module.params.get('action') + direction = self.module.params.get('direction') + bucket = self.module.params.get('bucket') + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + network_load_balancer_id = get_resource_id( + self.module, + ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( + datacenter_id, depth=1, + ), + self.module.params.get('network_load_balancer'), + ) - for option_name, option in OPTIONS.items(): - arguments[option_name] = { - 'type': option['type'], - } - for key in ['choices', 'default', 'aliases', 'no_log', 'elements']: - if option.get(key) is not None: - arguments[option_name][key] = option.get(key) + if existing_object is not None: + name = existing_object.properties.name if name is None else name + action = existing_object.properties.type if action is None else action + direction = existing_object.properties.direction if direction is None else direction + bucket = existing_object.properties.bucket if bucket is None else bucket - if option.get('env_fallback'): - arguments[option_name]['fallback'] = (env_fallback, [option['env_fallback']]) + network_loadbalancers_api = ionoscloud.NetworkLoadBalancersApi(client) + + nlb_flowlog_properties = FlowLogProperties(name=name, action=action, direction=direction, bucket=bucket) + nlb_flowlog = FlowLog(properties=nlb_flowlog_properties) - if len(option.get('required', [])) == len(STATES): - arguments[option_name]['required'] = True + try: + response, _, headers = network_loadbalancers_api.datacenters_networkloadbalancers_flowlogs_post_with_http_info( + datacenter_id, network_load_balancer_id, nlb_flowlog, + ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=int(self.module.params.get('wait_timeout'))) + except ApiException as e: + self.module.fail_json(msg="failed to create the new Network Loadbalancer Flowlog: %s" % to_native(e)) + return response - return arguments + def _update_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + action = self.module.params.get('action') + direction = self.module.params.get('direction') + bucket = self.module.params.get('bucket') + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + network_load_balancer_id = get_resource_id( + self.module, + ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( + datacenter_id, depth=1, + ), + self.module.params.get('network_load_balancer'), + ) -def get_sdk_config(module, sdk): - username = module.params.get('username') - password = module.params.get('password') - token = module.params.get('token') - api_url = module.params.get('api_url') - certificate_fingerprint = module.params.get('certificate_fingerprint') + network_loadbalancers_api = ionoscloud.NetworkLoadBalancersApi(client) - if token is not None: - # use the token instead of username & password - conf = { - 'token': token - } - else: - # use the username & password - conf = { - 'username': username, - 'password': password, - } + flowlog_properties = FlowLogProperties(name=name, action=action, direction=direction, bucket=bucket) - if api_url is not None: - conf['host'] = api_url - conf['server_index'] = None + try: + response, _, headers = network_loadbalancers_api.datacenters_networkloadbalancers_flowlogs_patch_with_http_info( + datacenter_id, network_load_balancer_id, existing_object.id, flowlog_properties, + ) - if certificate_fingerprint is not None: - conf['fingerprint'] = certificate_fingerprint + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=int(self.module.params.get('wait_timeout'))) - return sdk.Configuration(**conf) + return response + except ApiException as e: + self.module.fail_json(msg="failed to update the Network Loadbalancer Flowlog: %s" % to_native(e)) -def check_required_arguments(module, state, object_name): - # manually checking if token or username & password provided - if ( - not module.params.get("token") - and not (module.params.get("username") and module.params.get("password")) - ): - module.fail_json( - msg='Token or username & password are required for {object_name} state {state}'.format( - object_name=object_name, - state=state, + def _remove_object(self, existing_object, clients): + client = clients[0] + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + network_load_balancer_id = get_resource_id( + self.module, + ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( + datacenter_id, depth=1, ), + self.module.params.get('network_load_balancer'), ) - for option_name, option in OPTIONS.items(): - if state in option.get('required', []) and not module.params.get(option_name): - module.fail_json( - msg='{option_name} parameter is required for {object_name} state {state}'.format( - option_name=option_name, - object_name=object_name, - state=state, - ), - ) - - -def main(): - module = AnsibleModule(argument_spec=get_module_arguments(), supports_check_mode=True) - - if not HAS_SDK: - module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') - - state = module.params.get('state') - with ApiClient(get_sdk_config(module, ionoscloud)) as api_client: - api_client.user_agent = USER_AGENT - check_required_arguments(module, state, OBJECT_NAME) + network_loadbalancers_api = ionoscloud.NetworkLoadBalancersApi(client) try: - if state == 'absent': - module.exit_json(**remove_object(module, api_client)) - elif state == 'present': - module.exit_json(**create_object(module, api_client)) - elif state == 'update': - module.exit_json(**update_object(module, api_client)) - except Exception as e: - module.fail_json(msg='failed to set {object_name} state {state}: {error}'.format(object_name=OBJECT_NAME, error=to_native(e), state=state)) + _, _, headers = network_loadbalancers_api.datacenters_networkloadbalancers_flowlogs_delete_with_http_info( + datacenter_id, network_load_balancer_id, existing_object.id, + ) + + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=int(self.module.params.get('wait_timeout'))) + except ApiException as e: + self.module.fail_json(msg="failed to remove the Network Loadbalancer Flowlog: %s" % to_native(e)) if __name__ == '__main__': - main() + ionos_module = NetworkLoadBalancerFlowlogModule() + if not HAS_SDK: + ionos_module.module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') + ionos_module.main() diff --git a/plugins/modules/network_load_balancer_rule.py b/plugins/modules/network_load_balancer_rule.py index e6dae6e7..ecd5684a 100644 --- a/plugins/modules/network_load_balancer_rule.py +++ b/plugins/modules/network_load_balancer_rule.py @@ -6,26 +6,27 @@ __metaclass__ = type -import re -import copy -import yaml - HAS_SDK = True try: import ionoscloud from ionoscloud import __version__ as sdk_version - from ionoscloud.models import NetworkLoadBalancer, NetworkLoadBalancerProperties, NetworkLoadBalancerForwardingRule, \ - NetworkLoadBalancerForwardingRuleProperties + from ionoscloud.models import NetworkLoadBalancerForwardingRule, NetworkLoadBalancerForwardingRuleProperties from ionoscloud.rest import ApiException - from ionoscloud import ApiClient except ImportError: HAS_SDK = False from ansible import __version__ -from ansible.module_utils.basic import AnsibleModule, env_fallback +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_module import CommonIonosModule +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_methods import ( + get_module_arguments, _get_request_id, get_resource_id, +) +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_options import get_default_options + + ANSIBLE_METADATA = { 'metadata_version': '1.1', 'status': ['preview'], @@ -101,14 +102,7 @@ **get_default_options(STATES), } -def transform_for_documentation(val): - val['required'] = len(val.get('required', [])) == len(STATES) - del val['available'] - del val['type'] - return val - -DOCUMENTATION = ''' ---- +DOCUMENTATION = """ module: network_load_balancer_rule short_description: Create or destroy a Ionos Cloud NetworkLoadbalancer Flowlog rule. description: @@ -122,7 +116,7 @@ def transform_for_documentation(val): - "ionoscloud >= 6.0.2" author: - "IONOS Cloud SDK Team " -''' +""" EXAMPLE_PER_STATE = { 'present' : ''' @@ -169,56 +163,6 @@ def transform_for_documentation(val): ilowuerhfgwoqrghbqwoguh """ -uuid_match = re.compile('[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}', re.I) - - -def _get_matched_resources(resource_list, identity, identity_paths=None): - """ - Fetch and return a resource based on an identity supplied for it, if none or more than one matches - are found an error is printed and None is returned. - """ - - if identity_paths is None: - identity_paths = [['id'], ['properties', 'name']] - - def check_identity_method(resource): - resource_identity = [] - - for identity_path in identity_paths: - current = resource - for el in identity_path: - current = getattr(current, el) - resource_identity.append(current) - - return identity in resource_identity - - return list(filter(check_identity_method, resource_list.items)) - - -def get_resource(module, resource_list, identity, identity_paths=None): - matched_resources = _get_matched_resources(resource_list, identity, identity_paths) - - if len(matched_resources) == 1: - return matched_resources[0] - elif len(matched_resources) > 1: - module.fail_json(msg="found more resources of type {} for '{}'".format(resource_list.id, identity)) - else: - return None - - -def get_resource_id(module, resource_list, identity, identity_paths=None): - resource = get_resource(module, resource_list, identity, identity_paths) - return resource.id if resource is not None else None - - -def _get_request_id(headers): - match = re.search('/requests/([-A-Fa-f0-9]+)/', headers) - if match: - return match.group(1) - else: - raise Exception("Failed to extract request ID from response " - "header 'location': '{location}'".format(location=headers['location'])) - def _get_health_check(health_check_param): health_check = None @@ -236,387 +180,222 @@ def _get_health_check(health_check_param): return health_check -def _should_replace_object(module, existing_object): - return False - - -def _should_update_object(module, existing_object): - new_health_check = _get_health_check(module.params.get('health_check')) - - def sort_func(el): - return el['ip'], el['port'] - - if module.params.get('targets'): - existing_targets = sorted(map( - lambda x: { 'ip': x.ip, 'port': x.port, 'weight': x.weight }, - existing_object.properties.targets - ), key=sort_func) - new_targets = sorted(module.params.get('targets'), key=sort_func) - - return ( - module.params.get('name') is not None - and existing_object.properties.name != module.params.get('name') - or module.params.get('algorithm') is not None - and existing_object.properties.algorithm != module.params.get('algorithm') - or module.params.get('protocol') is not None - and existing_object.properties.protocol != module.params.get('protocol') - or module.params.get('listener_ip') is not None - and existing_object.properties.listener_ip != module.params.get('listener_ip') - or module.params.get('listener_port') is not None - and existing_object.properties.listener_port != module.params.get('listener_port') - or module.params.get('target_subnet') is not None - and existing_object.properties.target_subnet != module.params.get('target_subnet') - or module.params.get('target_port_range') is not None - and existing_object.properties.target_port_range != module.params.get('target_port_range') - or module.params.get('targets') is not None - and new_targets != existing_targets - or module.params.get('health_check') is not None - and ( - existing_object.properties.health_check.client_timeout != new_health_check.client_timeout - or existing_object.properties.health_check.connect_timeout != new_health_check.connect_timeout - or existing_object.properties.health_check.target_timeout != new_health_check.target_timeout - or existing_object.properties.health_check.retries != new_health_check.retries - ) - ) - - -def _get_object_list(module, client): - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - network_load_balancer_id = get_resource_id( - module, - ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( - datacenter_id, depth=1, - ), - module.params.get('network_load_balancer'), - ) - - return ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_forwardingrules_get( - datacenter_id, network_load_balancer_id, depth=1, - ) - - -def _get_object_name(module): - return module.params.get('name') - - -def _get_object_identifier(module): - return module.params.get('forwarding_rule') - - -def _create_object(module, client, existing_object=None): - name = module.params.get('name') - algorithm = module.params.get('algorithm') - protocol = module.params.get('protocol') - listener_ip = module.params.get('listener_ip') - listener_port = module.params.get('listener_port') - health_check_param = _get_health_check(module.params.get('health_check')) - targets = module.params.get('targets') - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - network_load_balancer_id = get_resource_id( - module, - ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( - datacenter_id, depth=1, - ), - module.params.get('network_load_balancer'), - ) - - if existing_object is not None: - name = existing_object.properties.name if name is None else name - algorithm = existing_object.properties.algorithm if algorithm is None else algorithm - protocol = existing_object.properties.protocol if protocol is None else protocol - listener_ip = existing_object.properties.listener_ip if listener_ip is None else listener_ip - listener_port = existing_object.properties.listener_port if listener_port is None else listener_port - health_check_param = existing_object.properties.health_check if health_check_param is None else health_check_param - targets = existing_object.properties.targets if targets is None else targets - - - nlbs_api = ionoscloud.NetworkLoadBalancersApi(client) - - nlb_forwarding_rule_properties = NetworkLoadBalancerForwardingRuleProperties( - name=name, algorithm=algorithm, - protocol=protocol, - listener_ip=listener_ip, - listener_port=listener_port, - health_check=health_check_param, - targets=targets, - ) - nlb_forwarding_rule = NetworkLoadBalancerForwardingRule(properties=nlb_forwarding_rule_properties) - - try: - response, _, headers = nlbs_api.datacenters_networkloadbalancers_forwardingrules_post_with_http_info( - datacenter_id, network_load_balancer_id, nlb_forwarding_rule, - ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - except ApiException as e: - module.fail_json(msg="failed to create the new Network Loadbalancer Rule: %s" % to_native(e)) - return response - - -def _update_object(module, client, existing_object): - name = module.params.get('name') - algorithm = module.params.get('algorithm') - protocol = module.params.get('protocol') - listener_ip = module.params.get('listener_ip') - listener_port = module.params.get('listener_port') - health_check = _get_health_check(module.params.get('health_check')) - targets = module.params.get('targets') - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - network_load_balancer_id = get_resource_id( - module, - ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( - datacenter_id, depth=1, - ), - module.params.get('network_load_balancer'), - ) - - nlbs_api = ionoscloud.NetworkLoadBalancersApi(client) - - nlb_forwarding_rule_properties = NetworkLoadBalancerForwardingRuleProperties( - name=name, algorithm=algorithm, - protocol=protocol, - listener_ip=listener_ip, - listener_port=listener_port, - health_check=health_check, - targets=targets, - ) - - try: - response, _, headers = nlbs_api.datacenters_networkloadbalancers_forwardingrules_patch_with_http_info( - datacenter_id, network_load_balancer_id, existing_object.id, nlb_forwarding_rule_properties, +class NetworkLoadBalancerRuleModule(CommonIonosModule): + def __init__(self) -> None: + super().__init__() + self.module = AnsibleModule(argument_spec=get_module_arguments(OPTIONS, STATES)) + self.returned_key = RETURNED_KEY + self.object_name = OBJECT_NAME + self.sdks = [ionoscloud] + self.user_agent = USER_AGENT + self.options = OPTIONS + + + def _should_replace_object(self, existing_object, clients): + return False + + + def _should_update_object(self, existing_object, clients): + new_health_check = _get_health_check(self.module.params.get('health_check')) + + def sort_func(el): + return el['ip'], el['port'] + + if self.module.params.get('targets'): + existing_targets = sorted(map( + lambda x: { 'ip': x.ip, 'port': x.port, 'weight': x.weight }, + existing_object.properties.targets + ), key=sort_func) + new_targets = sorted(self.module.params.get('targets'), key=sort_func) + + return ( + self.module.params.get('name') is not None + and existing_object.properties.name != self.module.params.get('name') + or self.module.params.get('algorithm') is not None + and existing_object.properties.algorithm != self.module.params.get('algorithm') + or self.module.params.get('protocol') is not None + and existing_object.properties.protocol != self.module.params.get('protocol') + or self.module.params.get('listener_ip') is not None + and existing_object.properties.listener_ip != self.module.params.get('listener_ip') + or self.module.params.get('listener_port') is not None + and existing_object.properties.listener_port != self.module.params.get('listener_port') + or self.module.params.get('target_subnet') is not None + and existing_object.properties.target_subnet != self.module.params.get('target_subnet') + or self.module.params.get('target_port_range') is not None + and existing_object.properties.target_port_range != self.module.params.get('target_port_range') + or self.module.params.get('targets') is not None + and new_targets != existing_targets + or self.module.params.get('health_check') is not None + and ( + existing_object.properties.health_check.client_timeout != new_health_check.client_timeout + or existing_object.properties.health_check.connect_timeout != new_health_check.connect_timeout + or existing_object.properties.health_check.target_timeout != new_health_check.target_timeout + or existing_object.properties.health_check.retries != new_health_check.retries + ) ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - return response - except ApiException as e: - module.fail_json(msg="failed to update the Network Loadbalancer Rule: %s" % to_native(e)) - - -def _remove_object(module, client, existing_object): - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - network_load_balancer_id = get_resource_id( - module, - ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( - datacenter_id, depth=1, - ), - module.params.get('network_load_balancer'), - ) - - nlbs_api = ionoscloud.NetworkLoadBalancersApi(client) - - try: - _, _, headers = nlbs_api.datacenters_networkloadbalancers_forwardingrules_delete_with_http_info( - datacenter_id, network_load_balancer_id, existing_object.id, + def _get_object_list(self, clients): + client = clients[0] + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - except ApiException as e: - module.fail_json(msg="failed to remove the Network Loadbalancer Rule: %s" % to_native(e)) - - -def update_replace_object(module, client, existing_object): - if _should_replace_object(module, existing_object): - - if not module.params.get('allow_replace'): - module.fail_json(msg="{} should be replaced but allow_replace is set to False.".format(OBJECT_NAME)) - - new_object = _create_object(module, client, existing_object).to_dict() - _remove_object(module, client, existing_object) - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: new_object, - } - if _should_update_object(module, existing_object): - # Update - return { - 'changed': True, - 'failed': False, - 'action': 'update', - RETURNED_KEY: _update_object(module, client, existing_object).to_dict() - } - - # No action - return { - 'changed': False, - 'failed': False, - 'action': 'create', - RETURNED_KEY: existing_object.to_dict() - } - - -def create_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_name(module)) - - if existing_object: - return update_replace_object(module, client, existing_object) - - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: _create_object(module, client).to_dict() - } - - -def update_object(module, client): - object_name = _get_object_name(module) - object_list = _get_object_list(module, client) - - existing_object = get_resource(module, object_list, _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return - - existing_object_id_by_new_name = get_resource_id(module, object_list, object_name) - - if ( - existing_object.id is not None - and existing_object_id_by_new_name is not None - and existing_object_id_by_new_name != existing_object.id - ): - module.fail_json( - msg='failed to update the {}: Another resource with the desired name ({}) exists'.format( - OBJECT_NAME, object_name, + network_load_balancer_id = get_resource_id( + self.module, + ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( + datacenter_id, depth=1, ), + self.module.params.get('network_load_balancer'), ) - return update_replace_object(module, client, existing_object) - - -def remove_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_identifier(module)) + return ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_forwardingrules_get( + datacenter_id, network_load_balancer_id, depth=1, + ) - if existing_object is None: - module.exit_json(changed=False) - return - _remove_object(module, client, existing_object) + def _get_object_name(self): + return self.module.params.get('name') - return { - 'action': 'delete', - 'changed': True, - 'id': existing_object.id, - } + def _get_object_identifier(self): + return self.module.params.get('forwarding_rule') -def get_module_arguments(): - arguments = {} - for option_name, option in OPTIONS.items(): - arguments[option_name] = { - 'type': option['type'], - } - for key in ['choices', 'default', 'aliases', 'no_log', 'elements']: - if option.get(key) is not None: - arguments[option_name][key] = option.get(key) + def _create_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + algorithm = self.module.params.get('algorithm') + protocol = self.module.params.get('protocol') + listener_ip = self.module.params.get('listener_ip') + listener_port = self.module.params.get('listener_port') + health_check_param = _get_health_check(self.module.params.get('health_check')) + targets = self.module.params.get('targets') + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + network_load_balancer_id = get_resource_id( + self.module, + ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( + datacenter_id, depth=1, + ), + self.module.params.get('network_load_balancer'), + ) - if option.get('env_fallback'): - arguments[option_name]['fallback'] = (env_fallback, [option['env_fallback']]) + if existing_object is not None: + name = existing_object.properties.name if name is None else name + algorithm = existing_object.properties.algorithm if algorithm is None else algorithm + protocol = existing_object.properties.protocol if protocol is None else protocol + listener_ip = existing_object.properties.listener_ip if listener_ip is None else listener_ip + listener_port = existing_object.properties.listener_port if listener_port is None else listener_port + health_check_param = existing_object.properties.health_check if health_check_param is None else health_check_param + targets = existing_object.properties.targets if targets is None else targets + + + nlbs_api = ionoscloud.NetworkLoadBalancersApi(client) + + nlb_forwarding_rule_properties = NetworkLoadBalancerForwardingRuleProperties( + name=name, algorithm=algorithm, + protocol=protocol, + listener_ip=listener_ip, + listener_port=listener_port, + health_check=health_check_param, + targets=targets, + ) + nlb_forwarding_rule = NetworkLoadBalancerForwardingRule(properties=nlb_forwarding_rule_properties) - if len(option.get('required', [])) == len(STATES): - arguments[option_name]['required'] = True + try: + response, _, headers = nlbs_api.datacenters_networkloadbalancers_forwardingrules_post_with_http_info( + datacenter_id, network_load_balancer_id, nlb_forwarding_rule, + ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) + except ApiException as e: + self.module.fail_json(msg="failed to create the new Network Loadbalancer Rule: %s" % to_native(e)) + return response - return arguments + def _update_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + algorithm = self.module.params.get('algorithm') + protocol = self.module.params.get('protocol') + listener_ip = self.module.params.get('listener_ip') + listener_port = self.module.params.get('listener_port') + health_check = _get_health_check(self.module.params.get('health_check')) + targets = self.module.params.get('targets') + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + network_load_balancer_id = get_resource_id( + self.module, + ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( + datacenter_id, depth=1, + ), + self.module.params.get('network_load_balancer'), + ) -def get_sdk_config(module, sdk): - username = module.params.get('username') - password = module.params.get('password') - token = module.params.get('token') - api_url = module.params.get('api_url') - certificate_fingerprint = module.params.get('certificate_fingerprint') + nlbs_api = ionoscloud.NetworkLoadBalancersApi(client) - if token is not None: - # use the token instead of username & password - conf = { - 'token': token - } - else: - # use the username & password - conf = { - 'username': username, - 'password': password, - } + nlb_forwarding_rule_properties = NetworkLoadBalancerForwardingRuleProperties( + name=name, algorithm=algorithm, + protocol=protocol, + listener_ip=listener_ip, + listener_port=listener_port, + health_check=health_check, + targets=targets, + ) - if api_url is not None: - conf['host'] = api_url - conf['server_index'] = None + try: + response, _, headers = nlbs_api.datacenters_networkloadbalancers_forwardingrules_patch_with_http_info( + datacenter_id, network_load_balancer_id, existing_object.id, nlb_forwarding_rule_properties, + ) - if certificate_fingerprint is not None: - conf['fingerprint'] = certificate_fingerprint + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) - return sdk.Configuration(**conf) + return response + except ApiException as e: + self.module.fail_json(msg="failed to update the Network Loadbalancer Rule: %s" % to_native(e)) -def check_required_arguments(module, state, object_name): - # manually checking if token or username & password provided - if ( - not module.params.get("token") - and not (module.params.get("username") and module.params.get("password")) - ): - module.fail_json( - msg='Token or username & password are required for {object_name} state {state}'.format( - object_name=object_name, - state=state, + def _remove_object(self, existing_object, clients): + client = clients[0] + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + network_load_balancer_id = get_resource_id( + self.module, + ionoscloud.NetworkLoadBalancersApi(client).datacenters_networkloadbalancers_get( + datacenter_id, depth=1, ), + self.module.params.get('network_load_balancer'), ) - for option_name, option in OPTIONS.items(): - if state in option.get('required', []) and not module.params.get(option_name): - module.fail_json( - msg='{option_name} parameter is required for {object_name} state {state}'.format( - option_name=option_name, - object_name=object_name, - state=state, - ), - ) - - -def main(): - module = AnsibleModule(argument_spec=get_module_arguments(), supports_check_mode=True) - - if not HAS_SDK: - module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') - - state = module.params.get('state') - with ApiClient(get_sdk_config(module, ionoscloud)) as api_client: - api_client.user_agent = USER_AGENT - check_required_arguments(module, state, OBJECT_NAME) + nlbs_api = ionoscloud.NetworkLoadBalancersApi(client) try: - if state == 'absent': - module.exit_json(**remove_object(module, api_client)) - elif state == 'present': - module.exit_json(**create_object(module, api_client)) - elif state == 'update': - module.exit_json(**update_object(module, api_client)) - except Exception as e: - module.fail_json(msg='failed to set {object_name} state {state}: {error}'.format(object_name=OBJECT_NAME, error=to_native(e), state=state)) + _, _, headers = nlbs_api.datacenters_networkloadbalancers_forwardingrules_delete_with_http_info( + datacenter_id, network_load_balancer_id, existing_object.id, + ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) + except ApiException as e: + self.module.fail_json(msg="failed to remove the Network Loadbalancer Rule: %s" % to_native(e)) if __name__ == '__main__': - main() + ionos_module = NetworkLoadBalancerRuleModule() + if not HAS_SDK: + ionos_module.module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') + ionos_module.main() diff --git a/plugins/modules/nic.py b/plugins/modules/nic.py index 4b6a6d41..41c29296 100644 --- a/plugins/modules/nic.py +++ b/plugins/modules/nic.py @@ -4,12 +4,6 @@ from __future__ import absolute_import, division, print_function -import copy -import re -import yaml - -from uuid import uuid4 - HAS_SDK = True try: @@ -17,14 +11,20 @@ from ionoscloud import __version__ as sdk_version from ionoscloud.models import Nic, NicProperties from ionoscloud.rest import ApiException - from ionoscloud import ApiClient except ImportError: HAS_SDK = False from ansible import __version__ -from ansible.module_utils.basic import AnsibleModule, env_fallback +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_module import CommonIonosModule +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_methods import ( + get_module_arguments, _get_request_id, get_resource_id, +) +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_options import get_default_options + + __metaclass__ = type ANSIBLE_METADATA = { @@ -107,16 +107,7 @@ **get_default_options(STATES), } - -def transform_for_documentation(val): - val['required'] = len(val.get('required', [])) == len(STATES) - del val['available'] - del val['type'] - return val - - -DOCUMENTATION = ''' ---- +DOCUMENTATION = """ module: nic short_description: Create, Update or Remove a NIC. description: @@ -129,7 +120,7 @@ def transform_for_documentation(val): - "ionoscloud >= 6.0.2" author: - "IONOS Cloud SDK Team " -''' +""" EXAMPLE_PER_STATE = { 'present': '''# Create a NIC @@ -174,431 +165,204 @@ def transform_for_documentation(val): ilowuerhfgwoqrghbqwoguh """ -uuid_match = re.compile( - '[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}', re.I) - - -def _get_matched_resources(resource_list, identity, identity_paths=None): - """ - Fetch and return a resource based on an identity supplied for it, if none or more than one matches - are found an error is printed and None is returned. - """ - - if identity_paths is None: - identity_paths = [['id'], ['properties', 'name']] - - def check_identity_method(resource): - resource_identity = [] - - for identity_path in identity_paths: - current = resource - for el in identity_path: - current = getattr(current, el) - resource_identity.append(current) - - return identity in resource_identity - - return list(filter(check_identity_method, resource_list.items)) - -def get_resource(module, resource_list, identity, identity_paths=None): - matched_resources = _get_matched_resources(resource_list, identity, identity_paths) - - if len(matched_resources) == 1: - return matched_resources[0] - elif len(matched_resources) > 1: - module.fail_json(msg="found more resources of type {} for '{}'".format(resource_list.id, identity)) - else: - return None - - -def get_resource_id(module, resource_list, identity, identity_paths=None): - resource = get_resource(module, resource_list, identity, identity_paths) - return resource.id if resource is not None else None - - -def _get_request_id(headers): - match = re.search('/requests/([-A-Fa-f0-9]+)/', headers) - if match: - return match.group(1) - else: - raise Exception("Failed to extract request ID from response " - "header 'location': '{location}'".format(location=headers['location'])) +class NicModule(CommonIonosModule): + def __init__(self) -> None: + super().__init__() + self.module = AnsibleModule(argument_spec=get_module_arguments(OPTIONS, STATES)) + self.returned_key = RETURNED_KEY + self.object_name = OBJECT_NAME + self.sdks = [ionoscloud] + self.user_agent = USER_AGENT + self.options = OPTIONS + + + def _should_replace_object(self, existing_object, clients): + return False + + + def _should_update_object(self, existing_object, clients): + return ( + self.module.params.get('ips') is not None + and sorted(existing_object.properties.ips) != sorted(self.module.params.get('ips')) + or self.module.params.get('ipv6_cidr') is not None + and existing_object.properties.ipv6_cidr_block != self.module.params.get('ipv6_cidr') + or self.module.params.get('ipv6_ips') is not None + and sorted(existing_object.properties.ipv6_ips) != sorted(self.module.params.get('ipv6_ips')) + or self.module.params.get('dhcp') is not None + and existing_object.properties.dhcp != self.module.params.get('dhcp') + or self.module.params.get('dhcpv6') is not None + and existing_object.properties.dhcpv6 != self.module.params.get('dhcpv6') + or self.module.params.get('lan') is not None + and int(existing_object.properties.lan) != int(self.module.params.get('lan')) + or self.module.params.get('firewall_active') is not None + and existing_object.properties.firewall_active != self.module.params.get('firewall_active') + or self.module.params.get('name') is not None + and existing_object.properties.name != self.module.params.get('name') + ) -def _should_replace_object(module, existing_object): - return False + def _get_object_list(self, clients): + client = clients[0] + datacenter = self.module.params.get('datacenter') + server = self.module.params.get('server') + datacenters_api = ionoscloud.DataCentersApi(api_client=client) + servers_api = ionoscloud.ServersApi(api_client=client) + nics_api = ionoscloud.NetworkInterfacesApi(api_client=client) -def _should_update_object(module, existing_object): - return ( - module.params.get('ips') is not None - and sorted(existing_object.properties.ips) != sorted(module.params.get('ips')) - or module.params.get('ipv6_cidr') is not None - and existing_object.properties.ipv6_cidr_block != module.params.get('ipv6_cidr') - or module.params.get('ipv6_ips') is not None - and sorted(existing_object.properties.ipv6_ips) != sorted(module.params.get('ipv6_ips')) - or module.params.get('dhcp') is not None - and existing_object.properties.dhcp != module.params.get('dhcp') - or module.params.get('dhcpv6') is not None - and existing_object.properties.dhcpv6 != module.params.get('dhcpv6') - or module.params.get('lan') is not None - and int(existing_object.properties.lan) != int(module.params.get('lan')) - or module.params.get('firewall_active') is not None - and existing_object.properties.firewall_active != module.params.get('firewall_active') - or module.params.get('name') is not None - and existing_object.properties.name != module.params.get('name') - ) + # Locate UUID for Datacenter + datacenter_list = datacenters_api.datacenters_get(depth=1) + datacenter = get_resource_id(self.module, datacenter_list, datacenter) + # Locate UUID for Server + server_list = servers_api.datacenters_servers_get(datacenter, depth=1) + server = get_resource_id(self.module, server_list, server) -def _get_object_list(module, client): - datacenter = module.params.get('datacenter') - server = module.params.get('server') + return nics_api.datacenters_servers_nics_get(datacenter_id=datacenter, server_id=server, depth=1) - datacenters_api = ionoscloud.DataCentersApi(api_client=client) - servers_api = ionoscloud.ServersApi(api_client=client) - nics_api = ionoscloud.NetworkInterfacesApi(api_client=client) - # Locate UUID for Datacenter - datacenter_list = datacenters_api.datacenters_get(depth=1) - datacenter = get_resource_id(module, datacenter_list, datacenter) + def _get_object_name(self): + return self.module.params.get('name') - # Locate UUID for Server - server_list = servers_api.datacenters_servers_get(datacenter, depth=1) - server = get_resource_id(module, server_list, server) - return nics_api.datacenters_servers_nics_get(datacenter_id=datacenter, server_id=server, depth=1) + def _get_object_identifier(self): + return self.module.params.get('nic') -def _get_object_name(module): - return module.params.get('name') + def _create_object(self, existing_object, clients): + client = clients[0] + lan = self.module.params.get('lan') + dhcp = self.module.params.get('dhcp') + dhcpv6 = self.module.params.get('dhcpv6') + firewall_active = self.module.params.get('firewall_active') + ips = self.module.params.get('ips') + ipv6_ips = self.module.params.get('ipv6_ips') + ipv6_cidr = self.module.params.get('ipv6_cidr') + name = self.module.params.get('name') + if existing_object is not None: + lan = existing_object.properties.lan if lan is None else lan + dhcp = existing_object.properties.dhcp if dhcp is None else dhcp + dhcpv6 = existing_object.properties.dhcpv6 if dhcpv6 is None else dhcpv6 + firewall_active = existing_object.properties.firewall_active if firewall_active is None else firewall_active + ips = existing_object.properties.ips if ips is None else ips + ipv6_ips = existing_object.properties.ipv6_ips if ipv6_ips is None else ipv6_ips + ipv6_cidr = existing_object.properties.ipv6_cidr_block if ipv6_cidr is None else ipv6_cidr + name = existing_object.properties.name if name is None else name + datacenters_api = ionoscloud.DataCentersApi(api_client=client) + servers_api = ionoscloud.ServersApi(api_client=client) + nics_api = ionoscloud.NetworkInterfacesApi(api_client=client) -def _get_object_identifier(module): - return module.params.get('nic') + # Locate UUID for Datacenter + datacenter_list = datacenters_api.datacenters_get(depth=1) + datacenter_id = get_resource_id(self.module, datacenter_list, self.module.params.get('datacenter')) + # Locate UUID for Server + server_list = servers_api.datacenters_servers_get(datacenter_id, depth=1) + server_id = get_resource_id(self.module, server_list, self.module.params.get('server')) -def _create_object(module, client, existing_object=None): - lan = module.params.get('lan') - dhcp = module.params.get('dhcp') - dhcpv6 = module.params.get('dhcpv6') - firewall_active = module.params.get('firewall_active') - ips = module.params.get('ips') - ipv6_ips = module.params.get('ipv6_ips') - ipv6_cidr = module.params.get('ipv6_cidr') - name = module.params.get('name') - if existing_object is not None: - lan = existing_object.properties.lan if lan is None else lan - dhcp = existing_object.properties.dhcp if dhcp is None else dhcp - dhcpv6 = existing_object.properties.dhcpv6 if dhcpv6 is None else dhcpv6 - firewall_active = existing_object.properties.firewall_active if firewall_active is None else firewall_active - ips = existing_object.properties.ips if ips is None else ips - ipv6_ips = existing_object.properties.ipv6_ips if ipv6_ips is None else ipv6_ips - ipv6_cidr = existing_object.properties.ipv6_cidr_block if ipv6_cidr is None else ipv6_cidr - name = existing_object.properties.name if name is None else name - - wait = module.params.get('wait') - wait_timeout = int(module.params.get('wait_timeout')) - - datacenters_api = ionoscloud.DataCentersApi(api_client=client) - servers_api = ionoscloud.ServersApi(api_client=client) - nics_api = ionoscloud.NetworkInterfacesApi(api_client=client) - - # Locate UUID for Datacenter - datacenter_list = datacenters_api.datacenters_get(depth=1) - datacenter_id = get_resource_id(module, datacenter_list, module.params.get('datacenter')) - - # Locate UUID for Server - server_list = servers_api.datacenters_servers_get(datacenter_id, depth=1) - server_id = get_resource_id(module, server_list, module.params.get('server')) - - nic = Nic(properties=NicProperties( - name=name, ips=ips, dhcp=dhcp, lan=lan, firewall_active=firewall_active, - dhcpv6=dhcpv6, ipv6_ips=ipv6_ips, ipv6_cidr_block=ipv6_cidr, - )) - - try: - nic_response, _, headers = nics_api.datacenters_servers_nics_post_with_http_info( - datacenter_id=datacenter_id, server_id=server_id, nic=nic, - ) + nic = Nic(properties=NicProperties( + name=name, ips=ips, dhcp=dhcp, lan=lan, firewall_active=firewall_active, + dhcpv6=dhcpv6, ipv6_ips=ipv6_ips, ipv6_cidr_block=ipv6_cidr, + )) - if wait: - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=wait_timeout) - nic_response = nics_api.datacenters_servers_nics_find_by_id( - datacenter_id=datacenter_id, server_id=server_id, nic_id=nic_response.id, - ) - except ApiException as e: - module.fail_json(msg="failed to create the new NIC: %s" % to_native(e)) - return nic_response - - -def _update_object(module, client, existing_object): - lan = module.params.get('lan') - dhcp = module.params.get('dhcp') - firewall_active = module.params.get('firewall_active') - ips = module.params.get('ips') - name = module.params.get('name') - dhcpv6 = module.params.get('dhcpv6') - ipv6_ips = module.params.get('ipv6_ips') - ipv6_cidr = module.params.get('ipv6_cidr') - - wait = module.params.get('wait') - wait_timeout = module.params.get('wait_timeout') - - datacenters_api = ionoscloud.DataCentersApi(api_client=client) - servers_api = ionoscloud.ServersApi(api_client=client) - nics_api = ionoscloud.NetworkInterfacesApi(api_client=client) - - # Locate UUID for Datacenter - datacenter_list = datacenters_api.datacenters_get(depth=1) - datacenter_id = get_resource_id(module, datacenter_list, module.params.get('datacenter')) - - # Locate UUID for Server - server_list = servers_api.datacenters_servers_get(datacenter_id, depth=1) - server_id = get_resource_id(module, server_list, module.params.get('server')) - - if lan is None: - lan = existing_object.properties.lan - if firewall_active is None: - firewall_active = existing_object.properties.firewall_active - if dhcp is None: - dhcp = existing_object.properties.dhcp - if dhcpv6 is None: - dhcpv6 = existing_object.properties.dhcpv6 - - nic_properties = NicProperties( - ips=ips, dhcp=dhcp, lan=lan, firewall_active=firewall_active, name=name, - dhcpv6=dhcpv6, ipv6_ips=ipv6_ips, ipv6_cidr_block=ipv6_cidr, - ) - - try: - nic_response, _, headers = nics_api.datacenters_servers_nics_patch_with_http_info( - datacenter_id=datacenter_id, server_id=server_id, nic_id=existing_object.id, nic=nic_properties, - ) - if wait: - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=wait_timeout) - nic_response = nics_api.datacenters_servers_nics_find_by_id( - datacenter_id=datacenter_id, server_id=server_id, nic_id=nic_response.id, + try: + nic_response, _, headers = nics_api.datacenters_servers_nics_post_with_http_info( + datacenter_id=datacenter_id, server_id=server_id, nic=nic, ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=int(self.module.params.get('wait_timeout'))) + nic_response = nics_api.datacenters_servers_nics_find_by_id( + datacenter_id=datacenter_id, server_id=server_id, nic_id=nic_response.id, + ) + except ApiException as e: + self.module.fail_json(msg="failed to create the new NIC: %s" % to_native(e)) return nic_response - except ApiException as e: - module.fail_json(msg="failed to update the NIC: %s" % to_native(e)) - - -def _remove_object(module, client, existing_object): - wait = module.params.get('wait') - wait_timeout = module.params.get('wait_timeout') - - datacenters_api = ionoscloud.DataCentersApi(api_client=client) - servers_api = ionoscloud.ServersApi(api_client=client) - nics_api = ionoscloud.NetworkInterfacesApi(api_client=client) - # Locate UUID for Datacenter - datacenter_list = datacenters_api.datacenters_get(depth=1) - datacenter_id = get_resource_id(module, datacenter_list, module.params.get('datacenter')) - # Locate UUID for Server - server_list = servers_api.datacenters_servers_get(datacenter_id, depth=1) - server_id = get_resource_id(module, server_list, module.params.get('server')) - - try: - _, _, headers = nics_api.datacenters_servers_nics_delete_with_http_info( - datacenter_id=datacenter_id, server_id=server_id, nic_id=existing_object.id, - ) - if wait: - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=wait_timeout) - except ApiException as e: - module.fail_json(msg="failed to remove the NIC: %s" % to_native(e)) - - -def update_replace_object(module, client, existing_object): - if _should_replace_object(module, existing_object): - - if not module.params.get('allow_replace'): - module.fail_json(msg="{} should be replaced but allow_replace is set to False.".format(OBJECT_NAME)) - - new_object = _create_object(module, client, existing_object).to_dict() - _remove_object(module, client, existing_object) - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: new_object, - } - if _should_update_object(module, existing_object): - # Update - return { - 'changed': True, - 'failed': False, - 'action': 'update', - RETURNED_KEY: _update_object(module, client, existing_object).to_dict() - } - - # No action - return { - 'changed': False, - 'failed': False, - 'action': 'create', - RETURNED_KEY: existing_object.to_dict() - } - - -def create_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_name(module)) - - if existing_object: - return update_replace_object(module, client, existing_object) - - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: _create_object(module, client).to_dict() - } - - -def update_object(module, client): - object_name = _get_object_name(module) - object_list = _get_object_list(module, client) - - existing_object = get_resource(module, object_list, _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return - - existing_object_id_by_new_name = get_resource_id(module, object_list, object_name) - - if ( - existing_object.id is not None - and existing_object_id_by_new_name is not None - and existing_object_id_by_new_name != existing_object.id - ): - module.fail_json( - msg='failed to update the {}: Another resource with the desired name ({}) exists'.format( - OBJECT_NAME, object_name, - ), - ) - - return update_replace_object(module, client, existing_object) - - -def remove_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return - - _remove_object(module, client, existing_object) - - return { - 'action': 'delete', - 'changed': True, - 'id': existing_object.id, - } - - -def get_module_arguments(): - arguments = {} - - for option_name, option in OPTIONS.items(): - arguments[option_name] = { - 'type': option['type'], - } - for key in ['choices', 'default', 'aliases', 'no_log', 'elements']: - if option.get(key) is not None: - arguments[option_name][key] = option.get(key) - - if option.get('env_fallback'): - arguments[option_name]['fallback'] = (env_fallback, [option['env_fallback']]) - - if len(option.get('required', [])) == len(STATES): - arguments[option_name]['required'] = True - - return arguments - - -def get_sdk_config(module, sdk): - username = module.params.get('username') - password = module.params.get('password') - token = module.params.get('token') - api_url = module.params.get('api_url') - certificate_fingerprint = module.params.get('certificate_fingerprint') - - if token is not None: - # use the token instead of username & password - conf = { - 'token': token - } - else: - # use the username & password - conf = { - 'username': username, - 'password': password, - } - - if api_url is not None: - conf['host'] = api_url - conf['server_index'] = None - - if certificate_fingerprint is not None: - conf['fingerprint'] = certificate_fingerprint - - return sdk.Configuration(**conf) - - -def check_required_arguments(module, state, object_name): - # manually checking if token or username & password provided - if ( - not module.params.get("token") - and not (module.params.get("username") and module.params.get("password")) - ): - module.fail_json( - msg='Token or username & password are required for {object_name} state {state}'.format( - object_name=object_name, - state=state, - ), + def _update_object(self, existing_object, clients): + client = clients[0] + lan = self.module.params.get('lan') + dhcp = self.module.params.get('dhcp') + firewall_active = self.module.params.get('firewall_active') + ips = self.module.params.get('ips') + name = self.module.params.get('name') + dhcpv6 = self.module.params.get('dhcpv6') + ipv6_ips = self.module.params.get('ipv6_ips') + ipv6_cidr = self.module.params.get('ipv6_cidr') + + datacenters_api = ionoscloud.DataCentersApi(api_client=client) + servers_api = ionoscloud.ServersApi(api_client=client) + nics_api = ionoscloud.NetworkInterfacesApi(api_client=client) + + # Locate UUID for Datacenter + datacenter_list = datacenters_api.datacenters_get(depth=1) + datacenter_id = get_resource_id(self.module, datacenter_list, self.module.params.get('datacenter')) + + # Locate UUID for Server + server_list = servers_api.datacenters_servers_get(datacenter_id, depth=1) + server_id = get_resource_id(self.module, server_list, self.module.params.get('server')) + + if lan is None: + lan = existing_object.properties.lan + if firewall_active is None: + firewall_active = existing_object.properties.firewall_active + if dhcp is None: + dhcp = existing_object.properties.dhcp + if dhcpv6 is None: + dhcpv6 = existing_object.properties.dhcpv6 + + nic_properties = NicProperties( + ips=ips, dhcp=dhcp, lan=lan, firewall_active=firewall_active, name=name, + dhcpv6=dhcpv6, ipv6_ips=ipv6_ips, ipv6_cidr_block=ipv6_cidr, ) - for option_name, option in OPTIONS.items(): - if state in option.get('required', []) and not module.params.get(option_name): - module.fail_json( - msg='{option_name} parameter is required for {object_name} state {state}'.format( - option_name=option_name, - object_name=object_name, - state=state, - ), + try: + nic_response, _, headers = nics_api.datacenters_servers_nics_patch_with_http_info( + datacenter_id=datacenter_id, server_id=server_id, nic_id=existing_object.id, nic=nic_properties, ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) + nic_response = nics_api.datacenters_servers_nics_find_by_id( + datacenter_id=datacenter_id, server_id=server_id, nic_id=nic_response.id, + ) + return nic_response + except ApiException as e: + self.module.fail_json(msg="failed to update the NIC: %s" % to_native(e)) -def main(): - module = AnsibleModule(argument_spec=get_module_arguments(), supports_check_mode=True) - if not HAS_SDK: - module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') + def _remove_object(self, existing_object, clients): + client = clients[0] + datacenters_api = ionoscloud.DataCentersApi(api_client=client) + servers_api = ionoscloud.ServersApi(api_client=client) + nics_api = ionoscloud.NetworkInterfacesApi(api_client=client) - state = module.params.get('state') - with ApiClient(get_sdk_config(module, ionoscloud)) as api_client: - api_client.user_agent = USER_AGENT - check_required_arguments(module, state, OBJECT_NAME) + # Locate UUID for Datacenter + datacenter_list = datacenters_api.datacenters_get(depth=1) + datacenter_id = get_resource_id(self.module, datacenter_list, self.module.params.get('datacenter')) + + # Locate UUID for Server + server_list = servers_api.datacenters_servers_get(datacenter_id, depth=1) + server_id = get_resource_id(self.module, server_list, self.module.params.get('server')) try: - if state == 'absent': - module.exit_json(**remove_object(module, api_client)) - elif state == 'present': - module.exit_json(**create_object(module, api_client)) - elif state == 'update': - module.exit_json(**update_object(module, api_client)) - except Exception as e: - module.fail_json(msg='failed to set {object_name} state {state}: {error}'.format(object_name=OBJECT_NAME, - error=to_native(e), - state=state)) + _, _, headers = nics_api.datacenters_servers_nics_delete_with_http_info( + datacenter_id=datacenter_id, server_id=server_id, nic_id=existing_object.id, + ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) + except ApiException as e: + self.module.fail_json(msg="failed to remove the NIC: %s" % to_native(e)) if __name__ == '__main__': - main() + ionos_module = NicModule() + if not HAS_SDK: + ionos_module.module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') + ionos_module.main() diff --git a/plugins/modules/nic_flowlog.py b/plugins/modules/nic_flowlog.py index e9929568..ffce9708 100644 --- a/plugins/modules/nic_flowlog.py +++ b/plugins/modules/nic_flowlog.py @@ -6,25 +6,27 @@ __metaclass__ = type -import copy -import re -import yaml - HAS_SDK = True try: import ionoscloud from ionoscloud import __version__ as sdk_version - from ionoscloud.models import FlowLog, FlowLogProperties, FlowLogPut + from ionoscloud.models import FlowLog, FlowLogProperties from ionoscloud.rest import ApiException - from ionoscloud import ApiClient except ImportError: HAS_SDK = False from ansible import __version__ -from ansible.module_utils.basic import AnsibleModule, env_fallback +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_module import CommonIonosModule +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_methods import ( + get_module_arguments, _get_request_id, get_resource_id, +) +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_options import get_default_options + + ANSIBLE_METADATA = { 'metadata_version': '1.1', 'status': ['preview'], @@ -88,16 +90,7 @@ **get_default_options(STATES), } - -def transform_for_documentation(val): - val['required'] = len(val.get('required', [])) == len(STATES) - del val['available'] - del val['type'] - return val - - -DOCUMENTATION = ''' ---- +DOCUMENTATION = """ module: datacenter short_description: Create or destroy a Ionos Cloud NIC Flowlog. description: @@ -111,7 +104,7 @@ def transform_for_documentation(val): - "ionoscloud >= 6.0.2" author: - "IONOS Cloud SDK Team " -''' +""" EXAMPLE_PER_STATE = { 'present': '''- name: Create a nic flowlog @@ -154,408 +147,190 @@ def transform_for_documentation(val): ilowuerhfgwoqrghbqwoguh """ -uuid_match = re.compile( - '[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}', re.I) - - -def _get_matched_resources(resource_list, identity, identity_paths=None): - """ - Fetch and return a resource based on an identity supplied for it, if none or more than one matches - are found an error is printed and None is returned. - """ - - if identity_paths is None: - identity_paths = [['id'], ['properties', 'name']] - - def check_identity_method(resource): - resource_identity = [] - - for identity_path in identity_paths: - current = resource - for el in identity_path: - current = getattr(current, el) - resource_identity.append(current) - - return identity in resource_identity - - return list(filter(check_identity_method, resource_list.items)) - - -def get_resource(module, resource_list, identity, identity_paths=None): - matched_resources = _get_matched_resources(resource_list, identity, identity_paths) - - if len(matched_resources) == 1: - return matched_resources[0] - elif len(matched_resources) > 1: - module.fail_json(msg="found more resources of type {} for '{}'".format(resource_list.id, identity)) - else: - return None - - -def get_resource_id(module, resource_list, identity, identity_paths=None): - resource = get_resource(module, resource_list, identity, identity_paths) - return resource.id if resource is not None else None - - -def _get_request_id(headers): - match = re.search('/requests/([-A-Fa-f0-9]+)/', headers) - if match: - return match.group(1) - else: - raise Exception("Failed to extract request ID from response " - "header 'location': '{location}'".format(location=headers['location'])) - -def _should_replace_object(module, existing_object): - return False - - -def _should_update_object(module, existing_object): - return ( - module.params.get('name') is not None - and existing_object.properties.name != module.params.get('name') - or module.params.get('action') is not None - and existing_object.properties.action != module.params.get('action') - or module.params.get('direction') is not None - and existing_object.properties.direction != module.params.get('direction') - or module.params.get('bucket') is not None - and existing_object.properties.bucket != module.params.get('bucket') - ) - - -def _get_object_list(module, client): - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - server_id = get_resource_id( - module, - ionoscloud.ServersApi(client).datacenters_servers_get(datacenter_id, depth=1), - module.params.get('server'), - ) - nic_id = get_resource_id( - module, - ionoscloud.NetworkInterfacesApi(client).datacenters_servers_nics_get( - datacenter_id, server_id, depth=1, - ), - module.params.get('nic'), - ) - - return ionoscloud.FlowLogsApi(client).datacenters_servers_nics_flowlogs_get( - datacenter_id=datacenter_id, server_id=server_id, nic_id=nic_id, depth=1 - ) - - -def _get_object_name(module): - return module.params.get('name') - - -def _get_object_identifier(module): - return module.params.get('flowlog') - - -def _create_object(module, client, existing_object=None): - name = module.params.get('name') - action = module.params.get('action') - direction = module.params.get('direction') - bucket = module.params.get('bucket') - if existing_object is not None: - action = existing_object.properties.action if action is None else action - direction = existing_object.properties.direction if direction is None else direction - bucket = existing_object.properties.bucket if bucket is None else bucket - name = existing_object.properties.name if name is None else name - - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - server_id = get_resource_id( - module, - ionoscloud.ServersApi(client).datacenters_servers_get(datacenter_id, depth=1), - module.params.get('server'), - ) - nic_id = get_resource_id( - module, - ionoscloud.NetworkInterfacesApi(client).datacenters_servers_nics_get( - datacenter_id, server_id, depth=1, - ), - module.params.get('nic'), - ) - - nic_flowlogs_api = ionoscloud.FlowLogsApi(client) - - flowlog = FlowLog(properties=FlowLogProperties(name=name, action=action, direction=direction, bucket=bucket)) - - try: - response, _, headers = nic_flowlogs_api.datacenters_servers_nics_flowlogs_post_with_http_info( - datacenter_id, server_id, nic_id, flowlog, +class NicFlowlogModule(CommonIonosModule): + def __init__(self) -> None: + super().__init__() + self.module = AnsibleModule(argument_spec=get_module_arguments(OPTIONS, STATES)) + self.returned_key = RETURNED_KEY + self.object_name = OBJECT_NAME + self.sdks = [ionoscloud] + self.user_agent = USER_AGENT + self.options = OPTIONS + + + def _should_replace_object(self, existing_object, clients): + return False + + + def _should_update_object(self, existing_object, clients): + return ( + self.module.params.get('name') is not None + and existing_object.properties.name != self.module.params.get('name') + or self.module.params.get('action') is not None + and existing_object.properties.action != self.module.params.get('action') + or self.module.params.get('direction') is not None + and existing_object.properties.direction != self.module.params.get('direction') + or self.module.params.get('bucket') is not None + and existing_object.properties.bucket != self.module.params.get('bucket') ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - except ApiException as e: - module.fail_json(msg="failed to create the new Flowlog: %s" % to_native(e)) - return response - - -def _update_object(module, client, existing_object): - name = module.params.get('name') - action = module.params.get('action') - direction = module.params.get('direction') - bucket = module.params.get('bucket') - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - server_id = get_resource_id( - module, - ionoscloud.ServersApi(client).datacenters_servers_get(datacenter_id, depth=1), - module.params.get('server'), - ) - nic_id = get_resource_id( - module, - ionoscloud.NetworkInterfacesApi(client).datacenters_servers_nics_get( - datacenter_id, server_id, depth=1, - ), - module.params.get('nic'), - ) - - nic_flowlogs_api = ionoscloud.FlowLogsApi(api_client=client) - - flowlog_properties = FlowLogProperties(name=name, action=action, direction=direction, bucket=bucket) - - try: - response, _, headers = nic_flowlogs_api.datacenters_servers_nics_flowlogs_patch_with_http_info( - datacenter_id, server_id, nic_id, existing_object.id, flowlog_properties, - ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - return response - except ApiException as e: - module.fail_json(msg="failed to update the Flowlog: %s" % to_native(e)) - - -def _remove_object(module, client, existing_object): - datacenter_id = get_resource_id( - module, - ionoscloud.DataCentersApi(client).datacenters_get(depth=1), - module.params.get('datacenter'), - ) - server_id = get_resource_id( - module, - ionoscloud.ServersApi(client).datacenters_servers_get(datacenter_id, depth=1), - module.params.get('server'), - ) - nic_id = get_resource_id( - module, - ionoscloud.NetworkInterfacesApi(client).datacenters_servers_nics_get( - datacenter_id, server_id, depth=1, - ), - module.params.get('nic'), - ) - - nic_flowlogs_api = ionoscloud.FlowLogsApi(api_client=client) - - try: - _, _, headers = nic_flowlogs_api.datacenters_servers_nics_flowlogs_delete_with_http_info( - datacenter_id, server_id, nic_id, existing_object.id, + def _get_object_list(self, clients): + client = clients[0] + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), ) - if module.params.get('wait'): - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=module.params.get('wait_timeout')) - except ApiException as e: - module.fail_json(msg="failed to remove the Flowlog: %s" % to_native(e)) - - -def update_replace_object(module, client, existing_object): - if _should_replace_object(module, existing_object): - - if not module.params.get('allow_replace'): - module.fail_json(msg="{} should be replaced but allow_replace is set to False.".format(OBJECT_NAME)) - - new_object = _create_object(module, client, existing_object).to_dict() - _remove_object(module, client, existing_object) - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: new_object, - } - if _should_update_object(module, existing_object): - # Update - return { - 'changed': True, - 'failed': False, - 'action': 'update', - RETURNED_KEY: _update_object(module, client, existing_object).to_dict() - } - - # No action - return { - 'changed': False, - 'failed': False, - 'action': 'create', - RETURNED_KEY: existing_object.to_dict() - } - - -def create_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_name(module)) - - if existing_object: - return update_replace_object(module, client, existing_object) - - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: _create_object(module, client).to_dict() - } - - -def update_object(module, client): - object_name = _get_object_name(module) - object_list = _get_object_list(module, client) - - existing_object = get_resource(module, object_list, _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return - - existing_object_id_by_new_name = get_resource_id(module, object_list, object_name) - - if ( - existing_object.id is not None - and existing_object_id_by_new_name is not None - and existing_object_id_by_new_name != existing_object.id - ): - module.fail_json( - msg='failed to update the {}: Another resource with the desired name ({}) exists'.format( - OBJECT_NAME, object_name, + server_id = get_resource_id( + self.module, + ionoscloud.ServersApi(client).datacenters_servers_get(datacenter_id, depth=1), + self.module.params.get('server'), + ) + nic_id = get_resource_id( + self.module, + ionoscloud.NetworkInterfacesApi(client).datacenters_servers_nics_get( + datacenter_id, server_id, depth=1, ), + self.module.params.get('nic'), ) - return update_replace_object(module, client, existing_object) + return ionoscloud.FlowLogsApi(client).datacenters_servers_nics_flowlogs_get( + datacenter_id=datacenter_id, server_id=server_id, nic_id=nic_id, depth=1 + ) -def remove_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_identifier(module)) + def _get_object_name(self): + return self.module.params.get('name') - if existing_object is None: - module.exit_json(changed=False) - return - _remove_object(module, client, existing_object) + def _get_object_identifier(self): + return self.module.params.get('flowlog') - return { - 'action': 'delete', - 'changed': True, - 'id': existing_object.id, - } + def _create_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + action = self.module.params.get('action') + direction = self.module.params.get('direction') + bucket = self.module.params.get('bucket') + if existing_object is not None: + action = existing_object.properties.action if action is None else action + direction = existing_object.properties.direction if direction is None else direction + bucket = existing_object.properties.bucket if bucket is None else bucket + name = existing_object.properties.name if name is None else name -def get_module_arguments(): - arguments = {} + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + server_id = get_resource_id( + self.module, + ionoscloud.ServersApi(client).datacenters_servers_get(datacenter_id, depth=1), + self.module.params.get('server'), + ) + nic_id = get_resource_id( + self.module, + ionoscloud.NetworkInterfacesApi(client).datacenters_servers_nics_get( + datacenter_id, server_id, depth=1, + ), + self.module.params.get('nic'), + ) - for option_name, option in OPTIONS.items(): - arguments[option_name] = { - 'type': option['type'], - } - for key in ['choices', 'default', 'aliases', 'no_log', 'elements']: - if option.get(key) is not None: - arguments[option_name][key] = option.get(key) + nic_flowlogs_api = ionoscloud.FlowLogsApi(client) - if option.get('env_fallback'): - arguments[option_name]['fallback'] = (env_fallback, [option['env_fallback']]) + flowlog = FlowLog(properties=FlowLogProperties(name=name, action=action, direction=direction, bucket=bucket)) - if len(option.get('required', [])) == len(STATES): - arguments[option_name]['required'] = True + try: + response, _, headers = nic_flowlogs_api.datacenters_servers_nics_flowlogs_post_with_http_info( + datacenter_id, server_id, nic_id, flowlog, + ) - return arguments + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) + except ApiException as e: + self.module.fail_json(msg="failed to create the new Flowlog: %s" % to_native(e)) + return response -def get_sdk_config(module, sdk): - username = module.params.get('username') - password = module.params.get('password') - token = module.params.get('token') - api_url = module.params.get('api_url') - certificate_fingerprint = module.params.get('certificate_fingerprint') + def _update_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + action = self.module.params.get('action') + direction = self.module.params.get('direction') + bucket = self.module.params.get('bucket') + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + server_id = get_resource_id( + self.module, + ionoscloud.ServersApi(client).datacenters_servers_get(datacenter_id, depth=1), + self.module.params.get('server'), + ) + nic_id = get_resource_id( + self.module, + ionoscloud.NetworkInterfacesApi(client).datacenters_servers_nics_get( + datacenter_id, server_id, depth=1, + ), + self.module.params.get('nic'), + ) - if token is not None: - # use the token instead of username & password - conf = { - 'token': token - } - else: - # use the username & password - conf = { - 'username': username, - 'password': password, - } + nic_flowlogs_api = ionoscloud.FlowLogsApi(api_client=client) - if api_url is not None: - conf['host'] = api_url - conf['server_index'] = None + flowlog_properties = FlowLogProperties(name=name, action=action, direction=direction, bucket=bucket) - if certificate_fingerprint is not None: - conf['fingerprint'] = certificate_fingerprint + try: + response, _, headers = nic_flowlogs_api.datacenters_servers_nics_flowlogs_patch_with_http_info( + datacenter_id, server_id, nic_id, existing_object.id, flowlog_properties, + ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) - return sdk.Configuration(**conf) + return response + except ApiException as e: + self.module.fail_json(msg="failed to update the Flowlog: %s" % to_native(e)) -def check_required_arguments(module, state, object_name): - # manually checking if token or username & password provided - if ( - not module.params.get("token") - and not (module.params.get("username") and module.params.get("password")) - ): - module.fail_json( - msg='Token or username & password are required for {object_name} state {state}'.format( - object_name=object_name, - state=state, + def _remove_object(self, existing_object, clients): + client = clients[0] + datacenter_id = get_resource_id( + self.module, + ionoscloud.DataCentersApi(client).datacenters_get(depth=1), + self.module.params.get('datacenter'), + ) + server_id = get_resource_id( + self.module, + ionoscloud.ServersApi(client).datacenters_servers_get(datacenter_id, depth=1), + self.module.params.get('server'), + ) + nic_id = get_resource_id( + self.module, + ionoscloud.NetworkInterfacesApi(client).datacenters_servers_nics_get( + datacenter_id, server_id, depth=1, ), + self.module.params.get('nic'), ) - for option_name, option in OPTIONS.items(): - if state in option.get('required', []) and not module.params.get(option_name): - module.fail_json( - msg='{option_name} parameter is required for {object_name} state {state}'.format( - option_name=option_name, - object_name=object_name, - state=state, - ), - ) - - -def main(): - module = AnsibleModule(argument_spec=get_module_arguments(), supports_check_mode=True) - - if not HAS_SDK: - module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') - - state = module.params.get('state') - with ApiClient(get_sdk_config(module, ionoscloud)) as api_client: - api_client.user_agent = USER_AGENT - check_required_arguments(module, state, OBJECT_NAME) + nic_flowlogs_api = ionoscloud.FlowLogsApi(api_client=client) try: - if state == 'absent': - module.exit_json(**remove_object(module, api_client)) - if state == 'present': - module.exit_json(**create_object(module, api_client)) - elif state == 'update': - module.exit_json(**update_object(module, api_client)) - except Exception as e: - module.fail_json(msg='failed to set {object_name} state {state}: {error}'.format(object_name=OBJECT_NAME, - error=to_native(e), - state=state)) + _, _, headers = nic_flowlogs_api.datacenters_servers_nics_flowlogs_delete_with_http_info( + datacenter_id, server_id, nic_id, existing_object.id, + ) + if self.module.params.get('wait'): + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=self.module.params.get('wait_timeout')) + except ApiException as e: + self.module.fail_json(msg="failed to remove the Flowlog: %s" % to_native(e)) if __name__ == '__main__': - main() + ionos_module = NicFlowlogModule() + if not HAS_SDK: + ionos_module.module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') + ionos_module.main() diff --git a/plugins/modules/pcc.py b/plugins/modules/pcc.py index f4588f07..4087889e 100644 --- a/plugins/modules/pcc.py +++ b/plugins/modules/pcc.py @@ -1,7 +1,3 @@ -import copy -import re -import yaml - HAS_SDK = True try: import ionoscloud @@ -9,14 +5,20 @@ from ionoscloud.models import PrivateCrossConnect from ionoscloud.models import PrivateCrossConnectProperties from ionoscloud.rest import ApiException - from ionoscloud import ApiClient except ImportError: HAS_SDK = False from ansible import __version__ -from ansible.module_utils.basic import AnsibleModule, env_fallback +from ansible.module_utils.basic import AnsibleModule from ansible.module_utils._text import to_native +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_module import CommonIonosModule +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_methods import ( + get_module_arguments, _get_request_id, +) +from ansible_collections.ionoscloudsdk.ionoscloud.plugins.module_utils.common_ionos_options import get_default_options + + ANSIBLE_METADATA = { 'metadata_version': '1.1', 'status': ['preview'], @@ -50,16 +52,7 @@ **get_default_options(STATES), } - -def transform_for_documentation(val): - val['required'] = len(val.get('required', [])) == len(STATES) - del val['available'] - del val['type'] - return val - - -DOCUMENTATION = ''' ---- +DOCUMENTATION = """ module: pcc short_description: Create or destroy a Ionos Cloud Cross Connect description: @@ -73,7 +66,7 @@ def transform_for_documentation(val): - "ionoscloud >= 6.0.2" author: - "IONOS Cloud SDK Team " -''' +""" EXAMPLE_PER_STATE = { 'present': ''' @@ -102,328 +95,110 @@ def transform_for_documentation(val): ilowuerhfgwoqrghbqwoguh """ +class PccModule(CommonIonosModule): + def __init__(self) -> None: + super().__init__() + self.module = AnsibleModule(argument_spec=get_module_arguments(OPTIONS, STATES)) + self.returned_key = RETURNED_KEY + self.object_name = OBJECT_NAME + self.sdks = [ionoscloud] + self.user_agent = USER_AGENT + self.options = OPTIONS -def _get_matched_resources(resource_list, identity, identity_paths=None): - """ - Fetch and return a resource based on an identity supplied for it, if none or more than one matches - are found an error is printed and None is returned. - """ - - if identity_paths is None: - identity_paths = [['id'], ['properties', 'name']] - - def check_identity_method(resource): - resource_identity = [] - - for identity_path in identity_paths: - current = resource - for el in identity_path: - current = getattr(current, el) - resource_identity.append(current) - - return identity in resource_identity - - return list(filter(check_identity_method, resource_list.items)) - - -def get_resource(module, resource_list, identity, identity_paths=None): - matched_resources = _get_matched_resources(resource_list, identity, identity_paths) - - if len(matched_resources) == 1: - return matched_resources[0] - elif len(matched_resources) > 1: - module.fail_json(msg="found more resources of type {} for '{}'".format(resource_list.id, identity)) - else: - return None - - -def get_resource_id(module, resource_list, identity, identity_paths=None): - resource = get_resource(module, resource_list, identity, identity_paths) - return resource.id if resource is not None else None - - -def _get_request_id(headers): - match = re.search('/requests/([-A-Fa-f0-9]+)/', headers) - if match: - return match.group(1) - else: - raise Exception("Failed to extract request ID from response " - "header 'location': '{location}'".format(location=headers['location'])) - - - -def _should_replace_object(module, existing_object): - return False - - -def _should_update_object(module, existing_object): - return ( - module.params.get('name') is not None - and existing_object.properties.name != module.params.get('name') - or module.params.get('description') is not None - and existing_object.properties.description != module.params.get('description') - ) - - -def _get_object_list(module, client): - return ionoscloud.PrivateCrossConnectsApi(client).pccs_get(depth=1) + def _should_replace_object(self, existing_object, clients): + return False -def _get_object_name(module): - return module.params.get('name') - -def _get_object_identifier(module): - return module.params.get('pcc') - - -def _create_object(module, client, existing_object=None): - name = module.params.get('name') - description = module.params.get('description') - if existing_object is not None: - name = existing_object.properties.name if name is None else name - description = existing_object.properties.description if description is None else description - - wait = module.params.get('wait') - wait_timeout = int(module.params.get('wait_timeout')) - - pccs_api = ionoscloud.PrivateCrossConnectsApi(client) - - pcc = PrivateCrossConnect(properties=PrivateCrossConnectProperties(name=name, description=description)) - - try: - pcc_response, _, headers = pccs_api.pccs_post_with_http_info(pcc) - if wait: - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=wait_timeout) - except ApiException as e: - module.fail_json(msg="failed to create the new PCC: %s" % to_native(e)) - return pcc_response - - -def _update_object(module, client, existing_object): - name = module.params.get('name') - description = module.params.get('description') - wait = module.params.get('wait') - wait_timeout = module.params.get('wait_timeout') - - pccs_api = ionoscloud.PrivateCrossConnectsApi(client) - - pcc_properties = PrivateCrossConnectProperties(name=name, description=description) - - try: - pcc_response, _, headers = pccs_api.pccs_patch_with_http_info( - pcc_id=existing_object.id, - pcc=pcc_properties, + def _should_update_object(self, existing_object, clients): + return ( + self.module.params.get('name') is not None + and existing_object.properties.name != self.module.params.get('name') + or self.module.params.get('description') is not None + and existing_object.properties.description != self.module.params.get('description') ) - if wait: - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=wait_timeout) - return pcc_response - except ApiException as e: - module.fail_json(msg="failed to update the PCC: %s" % to_native(e)) - - -def _remove_object(module, client, existing_object): - wait = module.params.get('wait') - wait_timeout = module.params.get('wait_timeout') - - pccs_api = ionoscloud.PrivateCrossConnectsApi(client) - - try: - _, _, headers = pccs_api.pccs_delete_with_http_info(existing_object.id) - if wait: - request_id = _get_request_id(headers['Location']) - client.wait_for_completion(request_id=request_id, timeout=wait_timeout) - except ApiException as e: - module.fail_json(msg="failed to remove the PCC: %s" % to_native(e)) - - -def update_replace_object(module, client, existing_object): - if _should_replace_object(module, existing_object): - - if not module.params.get('allow_replace'): - module.fail_json(msg="{} should be replaced but allow_replace is set to False.".format(OBJECT_NAME)) - - new_object = _create_object(module, client, existing_object).to_dict() - _remove_object(module, client, existing_object) - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: new_object, - } - if _should_update_object(module, existing_object): - # Update - return { - 'changed': True, - 'failed': False, - 'action': 'update', - RETURNED_KEY: _update_object(module, client, existing_object).to_dict() - } - - # No action - return { - 'changed': False, - 'failed': False, - 'action': 'create', - RETURNED_KEY: existing_object.to_dict() - } - - -def create_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_name(module)) - - if existing_object: - return update_replace_object(module, client, existing_object) - - return { - 'changed': True, - 'failed': False, - 'action': 'create', - RETURNED_KEY: _create_object(module, client).to_dict() - } - - -def update_object(module, client): - object_name = _get_object_name(module) - object_list = _get_object_list(module, client) - - existing_object = get_resource(module, object_list, _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return - - existing_object_id_by_new_name = get_resource_id(module, object_list, object_name) - - if ( - existing_object.id is not None - and existing_object_id_by_new_name is not None - and existing_object_id_by_new_name != existing_object.id - ): - module.fail_json( - msg='failed to update the {}: Another resource with the desired name ({}) exists'.format( - OBJECT_NAME, object_name, - ), - ) - - return update_replace_object(module, client, existing_object) - - -def remove_object(module, client): - existing_object = get_resource(module, _get_object_list(module, client), _get_object_identifier(module)) - - if existing_object is None: - module.exit_json(changed=False) - return - - _remove_object(module, client, existing_object) - return { - 'action': 'delete', - 'changed': True, - 'id': existing_object.id, - } + def _get_object_list(self, clients): + return ionoscloud.PrivateCrossConnectsApi(clients[0]).pccs_get(depth=1) -def get_module_arguments(): - arguments = {} + def _get_object_name(self): + return self.module.params.get('name') - for option_name, option in OPTIONS.items(): - arguments[option_name] = { - 'type': option['type'], - } - for key in ['choices', 'default', 'aliases', 'no_log', 'elements']: - if option.get(key) is not None: - arguments[option_name][key] = option.get(key) - if option.get('env_fallback'): - arguments[option_name]['fallback'] = (env_fallback, [option['env_fallback']]) + def _get_object_identifier(self): + return self.module.params.get('pcc') - if len(option.get('required', [])) == len(STATES): - arguments[option_name]['required'] = True - return arguments + def _create_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + description = self.module.params.get('description') + if existing_object is not None: + name = existing_object.properties.name if name is None else name + description = existing_object.properties.description if description is None else description + wait = self.module.params.get('wait') + wait_timeout = int(self.module.params.get('wait_timeout')) -def get_sdk_config(module, sdk): - username = module.params.get('username') - password = module.params.get('password') - token = module.params.get('token') - api_url = module.params.get('api_url') - certificate_fingerprint = module.params.get('certificate_fingerprint') + pccs_api = ionoscloud.PrivateCrossConnectsApi(client) - if token is not None: - # use the token instead of username & password - conf = { - 'token': token - } - else: - # use the username & password - conf = { - 'username': username, - 'password': password, - } + pcc = PrivateCrossConnect(properties=PrivateCrossConnectProperties(name=name, description=description)) - if api_url is not None: - conf['host'] = api_url - conf['server_index'] = None + try: + pcc_response, _, headers = pccs_api.pccs_post_with_http_info(pcc) + if wait: + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=wait_timeout) + except ApiException as e: + self.module.fail_json(msg="failed to create the new PCC: %s" % to_native(e)) + return pcc_response - if certificate_fingerprint is not None: - conf['fingerprint'] = certificate_fingerprint - return sdk.Configuration(**conf) + def _update_object(self, existing_object, clients): + client = clients[0] + name = self.module.params.get('name') + description = self.module.params.get('description') + wait = self.module.params.get('wait') + wait_timeout = self.module.params.get('wait_timeout') + pccs_api = ionoscloud.PrivateCrossConnectsApi(client) -def check_required_arguments(module, state, object_name): - # manually checking if token or username & password provided - if ( - not module.params.get("token") - and not (module.params.get("username") and module.params.get("password")) - ): - module.fail_json( - msg='Token or username & password are required for {object_name} state {state}'.format( - object_name=object_name, - state=state, - ), - ) + pcc_properties = PrivateCrossConnectProperties(name=name, description=description) - for option_name, option in OPTIONS.items(): - if state in option.get('required', []) and not module.params.get(option_name): - module.fail_json( - msg='{option_name} parameter is required for {object_name} state {state}'.format( - option_name=option_name, - object_name=object_name, - state=state, - ), + try: + pcc_response, _, headers = pccs_api.pccs_patch_with_http_info( + pcc_id=existing_object.id, + pcc=pcc_properties, ) + if wait: + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=wait_timeout) + return pcc_response + except ApiException as e: + self.module.fail_json(msg="failed to update the PCC: %s" % to_native(e)) -def main(): - module = AnsibleModule(argument_spec=get_module_arguments(), supports_check_mode=True) - if not HAS_SDK: - module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') + def _remove_object(self, existing_object, clients): + client = clients[0] + wait = self.module.params.get('wait') + wait_timeout = self.module.params.get('wait_timeout') - state = module.params.get('state') - with ApiClient(get_sdk_config(module, ionoscloud)) as api_client: - api_client.user_agent = USER_AGENT - check_required_arguments(module, state, OBJECT_NAME) + pccs_api = ionoscloud.PrivateCrossConnectsApi(client) try: - if state == 'present': - module.exit_json(**create_object(module, api_client)) - elif state == 'absent': - module.exit_json(**remove_object(module, api_client)) - elif state == 'update': - module.exit_json(**update_object(module, api_client)) - except Exception as e: - module.fail_json(msg='failed to set {object_name} state {state}: {error}'.format( - object_name=OBJECT_NAME, error=to_native(e), state=state, - )) + _, _, headers = pccs_api.pccs_delete_with_http_info(existing_object.id) + if wait: + request_id = _get_request_id(headers['Location']) + client.wait_for_completion(request_id=request_id, timeout=wait_timeout) + except ApiException as e: + self.module.fail_json(msg="failed to remove the PCC: %s" % to_native(e)) if __name__ == '__main__': - main() + ionos_module = PccModule() + if not HAS_SDK: + ionos_module.module.fail_json(msg='ionoscloud is required for this module, run `pip install ionoscloud`') + ionos_module.main()