From eefc90b66f7d02338a1484ce6a1350eddf640d05 Mon Sep 17 00:00:00 2001 From: Matt Clay Date: Mon, 13 May 2019 10:24:12 -0700 Subject: [PATCH] Revert oracle (#56364) * Revert "Update vcn module documentation (#55888)" This reverts commit 94ef24974a053c7eeee5ec3c4bd3f0aba00712e0. * Revert "Initial commit for Oracle Cloud Infrastructure modules (#53156)" This reverts commit 07b9f52fd5670ff85c2a36028e3d476882eaec83. * Revert "Add Oracle module maintainers (#55889)" This reverts commit b0f3df360aa0f99bd3878c844730f7c124c397d7. --- .github/BOTMETA.yml | 7 - lib/ansible/module_utils/oracle/__init__.py | 0 .../module_utils/oracle/oci_logging.yaml | 59 - lib/ansible/module_utils/oracle/oci_utils.py | 1982 ----------------- lib/ansible/modules/cloud/oracle/__init__.py | 0 lib/ansible/modules/cloud/oracle/oci_vcn.py | 225 -- lib/ansible/plugins/doc_fragments/oracle.py | 75 - .../oracle_creatable_resource.py | 23 - .../oracle_display_name_option.py | 15 - .../doc_fragments/oracle_name_option.py | 15 - .../plugins/doc_fragments/oracle_tags.py | 21 - .../doc_fragments/oracle_wait_options.py | 25 - 12 files changed, 2447 deletions(-) delete mode 100644 lib/ansible/module_utils/oracle/__init__.py delete mode 100644 lib/ansible/module_utils/oracle/oci_logging.yaml delete mode 100644 lib/ansible/module_utils/oracle/oci_utils.py delete mode 100644 lib/ansible/modules/cloud/oracle/__init__.py delete mode 100644 lib/ansible/modules/cloud/oracle/oci_vcn.py delete mode 100644 lib/ansible/plugins/doc_fragments/oracle.py delete mode 100644 lib/ansible/plugins/doc_fragments/oracle_creatable_resource.py delete mode 100644 lib/ansible/plugins/doc_fragments/oracle_display_name_option.py delete mode 100644 lib/ansible/plugins/doc_fragments/oracle_name_option.py delete mode 100644 lib/ansible/plugins/doc_fragments/oracle_tags.py delete mode 100644 lib/ansible/plugins/doc_fragments/oracle_wait_options.py diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index 1ffe303ab14..865160aa022 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -175,10 +175,6 @@ files: ignored: andyhky $modules/cloud/opennebula/: ilicmilan kustodian $modules/cloud/openstack/: $team_openstack - $modules/cloud/oracle/: &oracle - labels: [ cloud, oracle ] - maintainers: $team_oracle - support: community $modules/cloud/ovirt/: *ovirt $modules/cloud/profitbricks/: baldwinSPC $modules/cloud/rackspace/: @@ -817,7 +813,6 @@ files: maintainers: $team_openstack labels: - cloud - $module_utils/oracle/: *oracle $module_utils/ovirt.py: *ovirt $module_utils/postgres.py: *postgresql $module_utils/powershell: *windows_core @@ -1129,7 +1124,6 @@ files: $plugins/doc_fragments/xenserver.py: maintainers: bvitnik $plugins/doc_fragments/hpe3par.py: *hpe3par - $plugins/doc_fragments/oracle*: *oracle ############################### # plugins/filter $plugins/filter/: @@ -1505,7 +1499,6 @@ macros: team_onyx: anasbadaha samerd team_openstack: emonty juliakreger rcarrillocruz Shrews dagnello mnaser odyssey4me evrardjp cloudnull gtema team_openswitch: Qalthos gdpak - team_oracle: nalsaber manojmeda mross22 team_ovirt: machacekondra mwperina mnecas team_postgresql: amenonsen Andersson007 andytom Dorn- jbscalia kostiantyn-nemchenko kustodian matburt nerzhul sebasmannem tcraxs team_purestorage: sdodsley sile16 lionmax genegr raekins bannaych opslounge diff --git a/lib/ansible/module_utils/oracle/__init__.py b/lib/ansible/module_utils/oracle/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/lib/ansible/module_utils/oracle/oci_logging.yaml b/lib/ansible/module_utils/oracle/oci_logging.yaml deleted file mode 100644 index 587cec0e15c..00000000000 --- a/lib/ansible/module_utils/oracle/oci_logging.yaml +++ /dev/null @@ -1,59 +0,0 @@ ---- -# Copyright (c) 2017, 2018 Oracle and/or its affiliates. -# This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# Apache License v2.0 -# See LICENSE.TXT for details. -version: 1 -disable_existing_loggers: False -formatters: - simple: - format: "[%(asctime)s - %(name)s - %(lineno)s - %(funcName)s - %(levelname)s] - %(message)s" - -handlers: - console: - class: logging.StreamHandler - level: DEBUG - formatter: simple - stream: ext://sys.stdout - - info_file_handler: - class: logging.handlers.RotatingFileHandler - level: INFO - formatter: simple - filename: "{path}/oci_ansible_info_{date}.log" - maxBytes: 10485760 # 10MB - backupCount: 20 - encoding: utf8 - mode: "a" - - error_file_handler: - class: logging.handlers.RotatingFileHandler - level: ERROR - formatter: simple - filename: "{path}/oci_ansible_errors_{date}.log" - maxBytes: 10485760 # 10MB - backupCount: 20 - encoding: utf8 - mode: "a" - - debug_file_handler: - class: logging.handlers.RotatingFileHandler - level: DEBUG - formatter: simple - filename: "{path}/oci_ansible_debug_{date}.log" - maxBytes: 10485760 # 10MB - backupCount: 20 - encoding: utf8 - mode: "a" - -#loggers: - #any_specific_module e.g. oci_bucket: - #level: INFO - #handlers: [info_file_handler] - #propagate: no - -root: - level: DEBUG - handlers: [info_file_handler, debug_file_handler, error_file_handler] -... diff --git a/lib/ansible/module_utils/oracle/oci_utils.py b/lib/ansible/module_utils/oracle/oci_utils.py deleted file mode 100644 index 9a8e070cb36..00000000000 --- a/lib/ansible/module_utils/oracle/oci_utils.py +++ /dev/null @@ -1,1982 +0,0 @@ -# Copyright (c) 2017, 2018, 2019 Oracle and/or its affiliates. -# This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# Apache License v2.0 -# See LICENSE.TXT for details. -from __future__ import absolute_import - -import logging -import logging.config -import os -import tempfile -from datetime import datetime -from operator import eq - -import time - -try: - import yaml - - import oci - from oci.constants import HEADER_NEXT_PAGE - - from oci.exceptions import ( - InvalidConfig, - InvalidPrivateKey, - MissingPrivateKeyPassphrase, - ConfigFileNotFound, - ServiceError, - MaximumWaitTimeExceeded, - ) - from oci.identity.identity_client import IdentityClient - from oci.object_storage.models import CreateBucketDetails - from oci.object_storage.models import UpdateBucketDetails - from oci.retry import RetryStrategyBuilder - from oci.util import to_dict, Sentinel - - HAS_OCI_PY_SDK = True -except ImportError: - HAS_OCI_PY_SDK = False - - -from ansible.module_utils._text import to_bytes -from ansible.module_utils.six import iteritems - -__version__ = "1.6.0-dev" - -MAX_WAIT_TIMEOUT_IN_SECONDS = 1200 - -# If a resource is in one of these states it would be considered inactive -DEAD_STATES = [ - "TERMINATING", - "TERMINATED", - "FAULTY", - "FAILED", - "DELETING", - "DELETED", - "UNKNOWN_ENUM_VALUE", - "DETACHING", - "DETACHED", -] - -# If a resource is in one of these states it would be considered available -DEFAULT_READY_STATES = [ - "AVAILABLE", - "ACTIVE", - "RUNNING", - "PROVISIONED", - "ATTACHED", - "ASSIGNED", - "SUCCEEDED", - "PENDING_PROVIDER", -] - -# If a resource is in one of these states, it would be considered deleted -DEFAULT_TERMINATED_STATES = ["TERMINATED", "DETACHED", "DELETED"] - - -def get_common_arg_spec(supports_create=False, supports_wait=False): - """ - Return the common set of module arguments for all OCI cloud modules. - :param supports_create: Variable to decide whether to add options related to idempotency of create operation. - :param supports_wait: Variable to decide whether to add options related to waiting for completion. - :return: A dict with applicable module options. - """ - # Note: This method is used by most OCI ansible resource modules during initialization. When making changes to this - # method, ensure that no `oci` python sdk dependencies are introduced in this method. This ensures that the modules - # can check for absence of OCI Python SDK and fail with an appropriate message. Introducing an OCI dependency in - # this method would break that error handling logic. - common_args = dict( - config_file_location=dict(type="str"), - config_profile_name=dict(type="str", default="DEFAULT"), - api_user=dict(type="str"), - api_user_fingerprint=dict(type="str", no_log=True), - api_user_key_file=dict(type="str"), - api_user_key_pass_phrase=dict(type="str", no_log=True), - auth_type=dict( - type="str", - required=False, - choices=["api_key", "instance_principal"], - default="api_key", - ), - tenancy=dict(type="str"), - region=dict(type="str"), - ) - - if supports_create: - common_args.update( - key_by=dict(type="list"), - force_create=dict(type="bool", default=False), - ) - - if supports_wait: - common_args.update( - wait=dict(type="bool", default=True), - wait_timeout=dict( - type="int", default=MAX_WAIT_TIMEOUT_IN_SECONDS - ), - wait_until=dict(type="str"), - ) - - return common_args - - -def get_facts_module_arg_spec(filter_by_name=False): - # Note: This method is used by most OCI ansible fact modules during initialization. When making changes to this - # method, ensure that no `oci` python sdk dependencies are introduced in this method. This ensures that the modules - # can check for absence of OCI Python SDK and fail with an appropriate message. Introducing an OCI dependency in - # this method would break that error handling logic. - facts_module_arg_spec = get_common_arg_spec() - if filter_by_name: - facts_module_arg_spec.update(name=dict(type="str")) - else: - facts_module_arg_spec.update(display_name=dict(type="str")) - return facts_module_arg_spec - - -def get_oci_config(module, service_client_class=None): - """Return the OCI configuration to use for all OCI API calls. The effective OCI configuration is derived by merging - any overrides specified for configuration attributes through Ansible module options or environment variables. The - order of precedence for deriving the effective configuration dict is: - 1. If a config file is provided, use that to setup the initial config dict. - 2. If a config profile is specified, use that config profile to setup the config dict. - 3. For each authentication attribute, check if an override is provided either through - a. Ansible Module option - b. Environment variable - and override the value in the config dict in that order.""" - config = {} - - config_file = module.params.get("config_file_location") - _debug("Config file through module options - {0} ".format(config_file)) - if not config_file: - if "OCI_CONFIG_FILE" in os.environ: - config_file = os.environ["OCI_CONFIG_FILE"] - _debug( - "Config file through OCI_CONFIG_FILE environment variable - {0}".format( - config_file - ) - ) - else: - config_file = "~/.oci/config" - _debug("Config file (fallback) - {0} ".format(config_file)) - - config_profile = module.params.get("config_profile_name") - if not config_profile: - if "OCI_CONFIG_PROFILE" in os.environ: - config_profile = os.environ["OCI_CONFIG_PROFILE"] - else: - config_profile = "DEFAULT" - try: - config = oci.config.from_file( - file_location=config_file, profile_name=config_profile - ) - except ( - ConfigFileNotFound, - InvalidConfig, - InvalidPrivateKey, - MissingPrivateKeyPassphrase, - ) as ex: - if not _is_instance_principal_auth(module): - # When auth_type is not instance_principal, config file is required - module.fail_json(msg=str(ex)) - else: - _debug( - "Ignore {0} as the auth_type is set to instance_principal".format( - str(ex) - ) - ) - # if instance_principal auth is used, an empty 'config' map is used below. - - config["additional_user_agent"] = "Oracle-Ansible/{0}".format(__version__) - # Merge any overrides through other IAM options - _merge_auth_option( - config, - module, - module_option_name="api_user", - env_var_name="OCI_USER_ID", - config_attr_name="user", - ) - _merge_auth_option( - config, - module, - module_option_name="api_user_fingerprint", - env_var_name="OCI_USER_FINGERPRINT", - config_attr_name="fingerprint", - ) - _merge_auth_option( - config, - module, - module_option_name="api_user_key_file", - env_var_name="OCI_USER_KEY_FILE", - config_attr_name="key_file", - ) - _merge_auth_option( - config, - module, - module_option_name="api_user_key_pass_phrase", - env_var_name="OCI_USER_KEY_PASS_PHRASE", - config_attr_name="pass_phrase", - ) - _merge_auth_option( - config, - module, - module_option_name="tenancy", - env_var_name="OCI_TENANCY", - config_attr_name="tenancy", - ) - _merge_auth_option( - config, - module, - module_option_name="region", - env_var_name="OCI_REGION", - config_attr_name="region", - ) - - # Redirect calls to home region for IAM service. - do_not_redirect = module.params.get( - "do_not_redirect_to_home_region", False - ) or os.environ.get("OCI_IDENTITY_DO_NOT_REDIRECT_TO_HOME_REGION") - if service_client_class == IdentityClient and not do_not_redirect: - _debug("Region passed for module invocation - {0} ".format(config["region"])) - identity_client = IdentityClient(config) - region_subscriptions = identity_client.list_region_subscriptions( - config["tenancy"] - ).data - # Replace the region in the config with the home region. - [config["region"]] = [ - rs.region_name for rs in region_subscriptions if rs.is_home_region is True - ] - _debug( - "Setting region in the config to home region - {0} ".format( - config["region"] - ) - ) - - return config - - -def create_service_client(module, service_client_class): - """ - Creates a service client using the common module options provided by the user. - :param module: An AnsibleModule that represents user provided options for a Task - :param service_client_class: A class that represents a client to an OCI Service - :return: A fully configured client - """ - config = get_oci_config(module, service_client_class) - kwargs = {} - - if _is_instance_principal_auth(module): - try: - signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner() - except Exception as ex: - message = ( - "Failed retrieving certificates from localhost. Instance principal based authentication is only" - "possible from within OCI compute instances. Exception: {0}".format( - str(ex) - ) - ) - module.fail_json(msg=message) - - kwargs["signer"] = signer - - # XXX: Validate configuration -- this may be redundant, as all Client constructors perform a validation - try: - oci.config.validate_config(config, **kwargs) - except oci.exceptions.InvalidConfig as ic: - module.fail_json( - msg="Invalid OCI configuration. Exception: {0}".format(str(ic)) - ) - - # Create service client class with the signer - client = service_client_class(config, **kwargs) - - return client - - -def _is_instance_principal_auth(module): - # check if auth type is overridden via module params - instance_principal_auth = ( - "auth_type" in module.params - and module.params["auth_type"] == "instance_principal" - ) - if not instance_principal_auth: - instance_principal_auth = ( - "OCI_ANSIBLE_AUTH_TYPE" in os.environ - and os.environ["OCI_ANSIBLE_AUTH_TYPE"] == "instance_principal" - ) - return instance_principal_auth - - -def _merge_auth_option( - config, module, module_option_name, env_var_name, config_attr_name -): - """Merge the values for an authentication attribute from ansible module options and - environment variables with the values specified in a configuration file""" - _debug("Merging {0}".format(module_option_name)) - - auth_attribute = module.params.get(module_option_name) - _debug( - "\t Ansible module option {0} = {1}".format(module_option_name, auth_attribute) - ) - if not auth_attribute: - if env_var_name in os.environ: - auth_attribute = os.environ[env_var_name] - _debug( - "\t Environment variable {0} = {1}".format(env_var_name, auth_attribute) - ) - - # An authentication attribute has been provided through an env-variable or an ansible - # option and must override the corresponding attribute's value specified in the - # config file [profile]. - if auth_attribute: - _debug( - "Updating config attribute {0} -> {1} ".format( - config_attr_name, auth_attribute - ) - ) - config.update({config_attr_name: auth_attribute}) - - -def bucket_details_factory(bucket_details_type, module): - bucket_details = None - if bucket_details_type == "create": - bucket_details = CreateBucketDetails() - elif bucket_details_type == "update": - bucket_details = UpdateBucketDetails() - - bucket_details.compartment_id = module.params["compartment_id"] - bucket_details.name = module.params["name"] - bucket_details.public_access_type = module.params["public_access_type"] - bucket_details.metadata = module.params["metadata"] - - return bucket_details - - -def filter_resources(all_resources, filter_params): - if not filter_params: - return all_resources - filtered_resources = [] - filtered_resources.extend( - [ - resource - for resource in all_resources - for key, value in filter_params.items() - if getattr(resource, key) == value - ] - ) - return filtered_resources - - -def list_all_resources(target_fn, **kwargs): - """ - Return all resources after paging through all results returned by target_fn. If a `display_name` or `name` is - provided as a kwarg, then only resources matching the specified name are returned. - :param target_fn: The target OCI SDK paged function to call - :param kwargs: All arguments that the OCI SDK paged function expects - :return: List of all objects returned by target_fn - :raises ServiceError: When the Service returned an Error response - :raises MaximumWaitTimeExceededError: When maximum wait time is exceeded while invoking target_fn - """ - filter_params = None - try: - response = call_with_backoff(target_fn, **kwargs) - except ValueError as ex: - if "unknown kwargs" in str(ex): - if "display_name" in kwargs: - if kwargs["display_name"]: - filter_params = {"display_name": kwargs["display_name"]} - del kwargs["display_name"] - elif "name" in kwargs: - if kwargs["name"]: - filter_params = {"name": kwargs["name"]} - del kwargs["name"] - response = call_with_backoff(target_fn, **kwargs) - - existing_resources = response.data - while response.has_next_page: - kwargs.update(page=response.headers.get(HEADER_NEXT_PAGE)) - response = call_with_backoff(target_fn, **kwargs) - existing_resources += response.data - - # If the underlying SDK Service list* method doesn't support filtering by name or display_name, filter the resources - # and return the matching list of resources - return filter_resources(existing_resources, filter_params) - - -def _debug(s): - get_logger("oci_utils").debug(s) - - -def get_logger(module_name): - oci_logging = setup_logging() - return oci_logging.getLogger(module_name) - - -def setup_logging( - default_config_file="/etc/ansible/oci_logging.yaml", - default_level="INFO", - default_log_path=tempfile.gettempdir(), -): - """Setup logging configuration""" - env_config_file = "LOG_CONFIG" - env_log_path = "LOG_PATH" - env_log_level = "LOG_LEVEL" - - config_file = os.getenv(env_config_file, default_config_file) - log_path = os.getenv(env_log_path, default_log_path) - - files_handlers = ["debug_file_handler", "error_file_handler", "info_file_handler"] - - if os.path.exists(config_file): - with open(to_bytes(config_file), "rt") as f: - config = yaml.safe_load(f.read()) - for files_handler in files_handlers: - config["handlers"][files_handler]["filename"] = config["handlers"][ - files_handler - ]["filename"].format( - path=log_path, date=datetime.today().strftime("%d-%m-%Y") - ) - logging.config.dictConfig(config) - else: - log_level_str = os.getenv(env_log_level, default_level) - log_level = logging.getLevelName(log_level_str) - - log_file_path = os.path.join(tempfile.gettempdir(), "oci_ansible_module.log") - - logging.basicConfig(filename=log_file_path, filemode="a", level=log_level) - return logging - - -def check_and_update_attributes( - target_instance, attr_name, input_value, existing_value, changed -): - """ - This function checks the difference between two resource attributes of literal types and sets the attrbute - value in the target instance type holding the attribute. - :param target_instance: The instance which contains the attribute whose values to be compared - :param attr_name: Name of the attribute whose value required to be compared - :param input_value: The value of the attribute provided by user - :param existing_value: The value of the attribute in the existing resource - :param changed: Flag to indicate whether there is any difference between the values - :return: Returns a boolean value indicating whether there is any difference between the values - """ - if input_value is not None and not eq(input_value, existing_value): - changed = True - target_instance.__setattr__(attr_name, input_value) - else: - target_instance.__setattr__(attr_name, existing_value) - return changed - - -def check_and_update_resource( - resource_type, - get_fn, - kwargs_get, - update_fn, - primitive_params_update, - kwargs_non_primitive_update, - module, - update_attributes, - client=None, - sub_attributes_of_update_model=None, - wait_applicable=True, - states=None, -): - - """ - This function handles update operation on a resource. It checks whether update is required and accordingly returns - the resource and the changed status. - :param wait_applicable: Indicates if the resource support wait - :param client: The resource Client class to use to perform the wait checks. This param must be specified if - wait_applicable is True - :param resource_type: The type of the resource. e.g. "private_ip" - :param get_fn: Function used to get the resource. e.g. virtual_network_client.get_private_ip - :param kwargs_get: Dictionary containing the arguments to be used to call get function. - e.g. {"private_ip_id": module.params["private_ip_id"]} - :param update_fn: Function used to update the resource. e.g virtual_network_client.update_private_ip - :param primitive_params_update: List of primitive parameters used for update function. e.g. ['private_ip_id'] - :param kwargs_non_primitive_update: Dictionary containing the non-primitive arguments to be used to call get - function with key as the non-primitive argument type & value as the name of the non-primitive argument to be passed - to the update function. e.g. {UpdatePrivateIpDetails: "update_private_ip_details"} - :param module: Instance of AnsibleModule - :param update_attributes: Attributes in update model. - :param states: List of lifecycle states to watch for while waiting after create_fn is called. - e.g. [module.params['wait_until'], "FAULTY"] - :param sub_attributes_of_update_model: Dictionary of non-primitive sub-attributes of update model. for example, - {'services': [ServiceIdRequestDetails()]} as in UpdateServiceGatewayDetails. - :return: Returns a dictionary containing the "changed" status and the resource. - """ - try: - result = dict(changed=False) - attributes_to_update, resource = get_attr_to_update( - get_fn, kwargs_get, module, update_attributes - ) - - if attributes_to_update: - kwargs_update = get_kwargs_update( - attributes_to_update, - kwargs_non_primitive_update, - module, - primitive_params_update, - sub_attributes_of_update_model, - ) - resource = call_with_backoff(update_fn, **kwargs_update).data - if wait_applicable: - if client is None: - module.fail_json( - msg="wait_applicable is True, but client is not specified." - ) - resource = wait_for_resource_lifecycle_state( - client, module, True, kwargs_get, get_fn, None, resource, states - ) - result["changed"] = True - result[resource_type] = to_dict(resource) - return result - except ServiceError as ex: - module.fail_json(msg=ex.message) - - -def get_kwargs_update( - attributes_to_update, - kwargs_non_primitive_update, - module, - primitive_params_update, - sub_attributes_of_update_model=None, -): - kwargs_update = dict() - for param in primitive_params_update: - kwargs_update[param] = module.params[param] - for param in kwargs_non_primitive_update: - update_object = param() - for key in update_object.attribute_map: - if key in attributes_to_update: - if ( - sub_attributes_of_update_model - and key in sub_attributes_of_update_model - ): - setattr(update_object, key, sub_attributes_of_update_model[key]) - else: - setattr(update_object, key, module.params[key]) - kwargs_update[kwargs_non_primitive_update[param]] = update_object - return kwargs_update - - -def is_dictionary_subset(sub, super_dict): - """ - This function checks if `sub` dictionary is a subset of `super` dictionary. - :param sub: subset dictionary, for example user_provided_attr_value. - :param super_dict: super dictionary, for example resources_attr_value. - :return: True if sub is contained in super. - """ - for key in sub: - if sub[key] != super_dict[key]: - return False - return True - - -def are_lists_equal(s, t): - if s is None and t is None: - return True - - if (s is None and len(t) >= 0) or (t is None and len(s) >= 0) or (len(s) != len(t)): - return False - - if len(s) == 0: - return True - - s = to_dict(s) - t = to_dict(t) - - if type(s[0]) == dict: - # Handle list of dicts. Dictionary returned by the API may have additional keys. For example, a get call on - # service gateway has an attribute `services` which is a list of `ServiceIdResponseDetails`. This has a key - # `service_name` which is not provided in the list of `services` by a user while making an update call; only - # `service_id` is provided by the user in the update call. - sorted_s = sort_list_of_dictionary(s) - sorted_t = sort_list_of_dictionary(t) - for index, d in enumerate(sorted_s): - if not is_dictionary_subset(d, sorted_t[index]): - return False - return True - else: - # Handle lists of primitive types. - try: - for elem in s: - t.remove(elem) - except ValueError: - return False - return not t - - -def get_attr_to_update(get_fn, kwargs_get, module, update_attributes): - try: - resource = call_with_backoff(get_fn, **kwargs_get).data - except ServiceError as ex: - module.fail_json(msg=ex.message) - - attributes_to_update = [] - - for attr in update_attributes: - resources_attr_value = getattr(resource, attr, None) - user_provided_attr_value = module.params.get(attr, None) - - unequal_list_attr = ( - type(resources_attr_value) == list or type(user_provided_attr_value) == list - ) and not are_lists_equal(user_provided_attr_value, resources_attr_value) - unequal_attr = type(resources_attr_value) != list and to_dict( - resources_attr_value - ) != to_dict(user_provided_attr_value) - if unequal_list_attr or unequal_attr: - # only update if the user has explicitly provided a value for this attribute - # otherwise, no update is necessary because the user hasn't expressed a particular - # value for that attribute - if module.params.get(attr, None): - attributes_to_update.append(attr) - - return attributes_to_update, resource - - -def get_taggable_arg_spec(supports_create=False, supports_wait=False): - """ - Returns an arg_spec that is valid for taggable OCI resources. - :return: A dict that represents an ansible arg spec that builds over the common_arg_spec and adds free-form and - defined tags. - """ - tag_arg_spec = get_common_arg_spec(supports_create, supports_wait) - tag_arg_spec.update( - dict(freeform_tags=dict(type="dict"), defined_tags=dict(type="dict")) - ) - return tag_arg_spec - - -def add_tags_to_model_from_module(model, module): - """ - Adds free-form and defined tags from an ansible module to a resource model - :param model: A resource model instance that supports 'freeform_tags' and 'defined_tags' as attributes - :param module: An AnsibleModule representing the options provided by the user - :return: The updated model class with the tags specified by the user. - """ - freeform_tags = module.params.get("freeform_tags", None) - defined_tags = module.params.get("defined_tags", None) - return add_tags_to_model_class(model, freeform_tags, defined_tags) - - -def add_tags_to_model_class(model, freeform_tags, defined_tags): - """ - Add free-form and defined tags to a resource model. - :param model: A resource model instance that supports 'freeform_tags' and 'defined_tags' as attributes - :param freeform_tags: A dict representing the freeform_tags to be applied to the model - :param defined_tags: A dict representing the defined_tags to be applied to the model - :return: The updated model class with the tags specified by the user - """ - try: - if freeform_tags is not None: - _debug("Model {0} set freeform tags to {1}".format(model, freeform_tags)) - model.__setattr__("freeform_tags", freeform_tags) - - if defined_tags is not None: - _debug("Model {0} set defined tags to {1}".format(model, defined_tags)) - model.__setattr__("defined_tags", defined_tags) - except AttributeError as ae: - _debug("Model {0} doesn't support tags. Error {1}".format(model, ae)) - - return model - - -def check_and_create_resource( - resource_type, - create_fn, - kwargs_create, - list_fn, - kwargs_list, - module, - model, - existing_resources=None, - exclude_attributes=None, - dead_states=None, - default_attribute_values=None, - supports_sort_by_time_created=True, -): - """ - This function checks whether there is a resource with same attributes as specified in the module options. If not, - it creates and returns the resource. - :param resource_type: Type of the resource to be created. - :param create_fn: Function used in the module to handle create operation. The function should return a dict with - keys as resource & changed. - :param kwargs_create: Dictionary of parameters for create operation. - :param list_fn: List function in sdk to list all the resources of type resource_type. - :param kwargs_list: Dictionary of parameters for list operation. - :param module: Instance of AnsibleModule - :param model: Model used to create a resource. - :param exclude_attributes: The attributes which should not be used to distinguish the resource. e.g. display_name, - dns_label. - :param dead_states: List of states which can't transition to any of the usable states of the resource. This deafults - to ["TERMINATING", "TERMINATED", "FAULTY", "FAILED", "DELETING", "DELETED", "UNKNOWN_ENUM_VALUE"] - :param default_attribute_values: A dictionary containing default values for attributes. - :return: A dictionary containing the resource & the "changed" status. e.g. {"vcn":{x:y}, "changed":True} - """ - - if module.params.get("force_create", None): - _debug("Force creating {0}".format(resource_type)) - result = call_with_backoff(create_fn, **kwargs_create) - return result - - # Get the existing resources list sorted by creation time in descending order. Return the latest matching resource - # in case of multiple resource matches. - if exclude_attributes is None: - exclude_attributes = {} - if default_attribute_values is None: - default_attribute_values = {} - try: - if existing_resources is None: - if supports_sort_by_time_created: - kwargs_list["sort_by"] = "TIMECREATED" - existing_resources = list_all_resources(list_fn, **kwargs_list) - except ValueError: - # list_fn doesn't support sort_by, so remove the sort_by key in kwargs_list and retry - kwargs_list.pop("sort_by", None) - try: - existing_resources = list_all_resources(list_fn, **kwargs_list) - # Handle errors like 404 due to bad arguments to the list_all_resources call. - except ServiceError as ex: - module.fail_json(msg=ex.message) - except ServiceError as ex: - module.fail_json(msg=ex.message) - - result = dict() - - attributes_to_consider = _get_attributes_to_consider( - exclude_attributes, model, module - ) - if "defined_tags" not in default_attribute_values: - default_attribute_values["defined_tags"] = {} - resource_matched = None - _debug( - "Trying to find a match within {0} existing resources".format( - len(existing_resources) - ) - ) - - for resource in existing_resources: - if _is_resource_active(resource, dead_states): - _debug( - "Comparing user specified values {0} against an existing resource's " - "values {1}".format(module.params, to_dict(resource)) - ) - if does_existing_resource_match_user_inputs( - to_dict(resource), - module, - attributes_to_consider, - exclude_attributes, - default_attribute_values, - ): - resource_matched = to_dict(resource) - break - - if resource_matched: - _debug("Resource with same attributes found: {0}.".format(resource_matched)) - result[resource_type] = resource_matched - result["changed"] = False - else: - _debug("No matching resource found. Attempting to create a new resource.") - result = call_with_backoff(create_fn, **kwargs_create) - - return result - - -def _get_attributes_to_consider(exclude_attributes, model, module): - """ - Determine the attributes to detect if an existing resource already matches the requested resource state - :param exclude_attributes: Attributes to not consider for matching - :param model: The model class used to create the Resource - :param module: An instance of AnsibleModule that contains user's desires around a resource's state - :return: A list of attributes that needs to be matched - """ - - # If a user explicitly requests us to match only against a set of resources (using 'key_by', use that as the list - # of attributes to consider for matching. - if "key_by" in module.params and module.params["key_by"] is not None: - attributes_to_consider = module.params["key_by"] - else: - # Consider all attributes except freeform_tags as freeform tags do not distinguish a resource. - attributes_to_consider = list(model.attribute_map) - if "freeform_tags" in attributes_to_consider: - attributes_to_consider.remove("freeform_tags") - # Temporarily removing node_count as the exisiting resource does not reflect it - if "node_count" in attributes_to_consider: - attributes_to_consider.remove("node_count") - _debug("attributes to consider: {0}".format(attributes_to_consider)) - return attributes_to_consider - - -def _is_resource_active(resource, dead_states): - if dead_states is None: - dead_states = DEAD_STATES - - if "lifecycle_state" not in resource.attribute_map: - return True - return resource.lifecycle_state not in dead_states - - -def is_attr_assigned_default(default_attribute_values, attr, assigned_value): - if not default_attribute_values: - return False - - if attr in default_attribute_values: - default_val_for_attr = default_attribute_values.get(attr, None) - if isinstance(default_val_for_attr, dict): - # When default value for a resource's attribute is empty dictionary, check if the corresponding value of the - # existing resource's attribute is also empty. - if not default_val_for_attr: - return not assigned_value - # only compare keys that are in default_attribute_values[attr] - # this is to ensure forward compatibility when the API returns new keys that are not known during - # the time when the module author provided default values for the attribute - keys = {} - for k, v in iteritems(assigned_value.items()): - if k in default_val_for_attr: - keys[k] = v - - return default_val_for_attr == keys - # non-dict, normal comparison - return default_val_for_attr == assigned_value - else: - # module author has not provided a default value for attr - return True - - -def create_resource(resource_type, create_fn, kwargs_create, module): - """ - Create an OCI resource - :param resource_type: Type of the resource to be created. e.g.: "vcn" - :param create_fn: Function in the SDK to create the resource. e.g. virtual_network_client.create_vcn - :param kwargs_create: Dictionary containing arguments to be used to call the create function create_fn - :param module: Instance of AnsibleModule - """ - result = dict(changed=False) - try: - resource = to_dict(call_with_backoff(create_fn, **kwargs_create).data) - _debug("Created {0}, {1}".format(resource_type, resource)) - result["changed"] = True - result[resource_type] = resource - return result - except (ServiceError, TypeError) as ex: - module.fail_json(msg=str(ex)) - - -def does_existing_resource_match_user_inputs( - existing_resource, - module, - attributes_to_compare, - exclude_attributes, - default_attribute_values=None, -): - """ - Check if 'attributes_to_compare' in an existing_resource match the desired state provided by a user in 'module'. - :param existing_resource: A dictionary representing an existing resource's values. - :param module: The AnsibleModule representing the options provided by the user. - :param attributes_to_compare: A list of attributes of a resource that are used to compare if an existing resource - matches the desire state of the resource expressed by the user in 'module'. - :param exclude_attributes: The attributes, that a module author provides, which should not be used to match the - resource. This dictionary typically includes: (a) attributes which are initialized with dynamic default values - like 'display_name', 'security_list_ids' for subnets and (b) attributes that don't have any defaults like - 'dns_label' in VCNs. The attributes are part of keys and 'True' is the value for all existing keys. - :param default_attribute_values: A dictionary containing default values for attributes. - :return: True if the values for the list of attributes is the same in the existing_resource and module instances. - """ - if not default_attribute_values: - default_attribute_values = {} - for attr in attributes_to_compare: - attribute_with_default_metadata = None - if attr in existing_resource: - resources_value_for_attr = existing_resource[attr] - # Check if the user has explicitly provided the value for attr. - user_provided_value_for_attr = _get_user_provided_value(module, attr) - if user_provided_value_for_attr is not None: - res = [True] - check_if_user_value_matches_resources_attr( - attr, - resources_value_for_attr, - user_provided_value_for_attr, - exclude_attributes, - default_attribute_values, - res, - ) - if not res[0]: - _debug( - "Mismatch on attribute '{0}'. User provided value is {1} & existing resource's value" - "is {2}.".format( - attr, user_provided_value_for_attr, resources_value_for_attr - ) - ) - return False - else: - # If the user has not explicitly provided the value for attr and attr is in exclude_list, we can - # consider this as a 'pass'. For example, if an attribute 'display_name' is not specified by user and - # that attribute is in the 'exclude_list' according to the module author(Not User), then exclude - if ( - exclude_attributes.get(attr) is None - and resources_value_for_attr is not None - ): - if module.argument_spec.get(attr): - attribute_with_default_metadata = module.argument_spec.get(attr) - default_attribute_value = attribute_with_default_metadata.get( - "default", None - ) - if default_attribute_value is not None: - if existing_resource[attr] != default_attribute_value: - return False - # Check if attr has a value that is not default. For example, a custom `security_list_id` - # is assigned to the subnet's attribute `security_list_ids`. If the attribute is assigned a - # value that is not the default, then it must be considered a mismatch and false returned. - elif not is_attr_assigned_default( - default_attribute_values, attr, existing_resource[attr] - ): - return False - - else: - _debug( - "Attribute {0} is in the create model of resource {1}" - "but doesn't exist in the get model of the resource".format( - attr, existing_resource.__class__ - ) - ) - return True - - -def tuplize(d): - """ - This function takes a dictionary and converts it to a list of tuples recursively. - :param d: A dictionary. - :return: List of tuples. - """ - list_of_tuples = [] - key_list = sorted(list(d.keys())) - for key in key_list: - if type(d[key]) == list: - # Convert a value which is itself a list of dict to a list of tuples. - if d[key] and type(d[key][0]) == dict: - sub_tuples = [] - for sub_dict in d[key]: - sub_tuples.append(tuplize(sub_dict)) - # To handle comparing two None values, while creating a tuple for a {key: value}, make the first element - # in the tuple a boolean `True` if value is None so that attributes with None value are put at last - # in the sorted list. - list_of_tuples.append((sub_tuples is None, key, sub_tuples)) - else: - list_of_tuples.append((d[key] is None, key, d[key])) - elif type(d[key]) == dict: - tupled_value = tuplize(d[key]) - list_of_tuples.append((tupled_value is None, key, tupled_value)) - else: - list_of_tuples.append((d[key] is None, key, d[key])) - return list_of_tuples - - -def get_key_for_comparing_dict(d): - tuple_form_of_d = tuplize(d) - return tuple_form_of_d - - -def sort_dictionary(d): - """ - This function sorts values of a dictionary recursively. - :param d: A dictionary. - :return: Dictionary with sorted elements. - """ - sorted_d = {} - for key in d: - if type(d[key]) == list: - if d[key] and type(d[key][0]) == dict: - sorted_value = sort_list_of_dictionary(d[key]) - sorted_d[key] = sorted_value - else: - sorted_d[key] = sorted(d[key]) - elif type(d[key]) == dict: - sorted_d[key] = sort_dictionary(d[key]) - else: - sorted_d[key] = d[key] - return sorted_d - - -def sort_list_of_dictionary(list_of_dict): - """ - This functions sorts a list of dictionaries. It first sorts each value of the dictionary and then sorts the list of - individually sorted dictionaries. For sorting, each dictionary's tuple equivalent is used. - :param list_of_dict: List of dictionaries. - :return: A sorted dictionary. - """ - list_with_sorted_dict = [] - for d in list_of_dict: - sorted_d = sort_dictionary(d) - list_with_sorted_dict.append(sorted_d) - return sorted(list_with_sorted_dict, key=get_key_for_comparing_dict) - - -def check_if_user_value_matches_resources_attr( - attribute_name, - resources_value_for_attr, - user_provided_value_for_attr, - exclude_attributes, - default_attribute_values, - res, -): - if isinstance(default_attribute_values.get(attribute_name), dict): - default_attribute_values = default_attribute_values.get(attribute_name) - - if isinstance(exclude_attributes.get(attribute_name), dict): - exclude_attributes = exclude_attributes.get(attribute_name) - - if isinstance(resources_value_for_attr, list) or isinstance( - user_provided_value_for_attr, list - ): - # Perform a deep equivalence check for a List attribute - if exclude_attributes.get(attribute_name): - return - if ( - user_provided_value_for_attr is None - and default_attribute_values.get(attribute_name) is not None - ): - user_provided_value_for_attr = default_attribute_values.get(attribute_name) - - if resources_value_for_attr is None and user_provided_value_for_attr is None: - return - - if ( - resources_value_for_attr is None - and len(user_provided_value_for_attr) >= 0 - or user_provided_value_for_attr is None - and len(resources_value_for_attr) >= 0 - ): - res[0] = False - return - - if ( - resources_value_for_attr is not None - and user_provided_value_for_attr is not None - and len(resources_value_for_attr) != len(user_provided_value_for_attr) - ): - res[0] = False - return - - if ( - user_provided_value_for_attr - and type(user_provided_value_for_attr[0]) == dict - ): - # Process a list of dict - sorted_user_provided_value_for_attr = sort_list_of_dictionary( - user_provided_value_for_attr - ) - sorted_resources_value_for_attr = sort_list_of_dictionary( - resources_value_for_attr - ) - - else: - sorted_user_provided_value_for_attr = sorted(user_provided_value_for_attr) - sorted_resources_value_for_attr = sorted(resources_value_for_attr) - - # Walk through the sorted list values of the resource's value for this attribute, and compare against user - # provided values. - for index, resources_value_for_attr_part in enumerate( - sorted_resources_value_for_attr - ): - check_if_user_value_matches_resources_attr( - attribute_name, - resources_value_for_attr_part, - sorted_user_provided_value_for_attr[index], - exclude_attributes, - default_attribute_values, - res, - ) - - elif isinstance(resources_value_for_attr, dict): - # Perform a deep equivalence check for dict typed attributes - - if not resources_value_for_attr and user_provided_value_for_attr: - res[0] = False - for key in resources_value_for_attr: - if ( - user_provided_value_for_attr is not None - and user_provided_value_for_attr - ): - check_if_user_value_matches_resources_attr( - key, - resources_value_for_attr.get(key), - user_provided_value_for_attr.get(key), - exclude_attributes, - default_attribute_values, - res, - ) - else: - if exclude_attributes.get(key) is None: - if default_attribute_values.get(key) is not None: - user_provided_value_for_attr = default_attribute_values.get(key) - check_if_user_value_matches_resources_attr( - key, - resources_value_for_attr.get(key), - user_provided_value_for_attr, - exclude_attributes, - default_attribute_values, - res, - ) - else: - res[0] = is_attr_assigned_default( - default_attribute_values, - attribute_name, - resources_value_for_attr.get(key), - ) - - elif resources_value_for_attr != user_provided_value_for_attr: - if ( - exclude_attributes.get(attribute_name) is None - and default_attribute_values.get(attribute_name) is not None - ): - # As the user has not specified a value for an optional attribute, if the existing resource's - # current state has a DEFAULT value for that attribute, we must not consider this incongruence - # an issue and continue with other checks. If the existing resource's value for the attribute - # is not the default value, then the existing resource is not a match. - if not is_attr_assigned_default( - default_attribute_values, attribute_name, resources_value_for_attr - ): - res[0] = False - elif user_provided_value_for_attr is not None: - res[0] = False - - -def are_dicts_equal( - option_name, - existing_resource_dict, - user_provided_dict, - exclude_list, - default_attribute_values, -): - if not user_provided_dict: - # User has not provided a value for the map option. In this case, the user hasn't expressed an intent around - # this optional attribute. Check if existing_resource_dict matches default. - # For example, source_details attribute in volume is optional and does not have any defaults. - return is_attr_assigned_default( - default_attribute_values, option_name, existing_resource_dict - ) - - # If the existing resource has an empty dict, while the user has provided entries, dicts are not equal - if not existing_resource_dict and user_provided_dict: - return False - - # check if all keys of an existing resource's dict attribute matches user-provided dict's entries - for sub_attr in existing_resource_dict: - # If user has provided value for sub-attribute, then compare it with corresponding key in existing resource. - if sub_attr in user_provided_dict: - if existing_resource_dict[sub_attr] != user_provided_dict[sub_attr]: - _debug( - "Failed to match: Existing resource's attr {0} sub-attr {1} value is {2}, while user " - "provided value is {3}".format( - option_name, - sub_attr, - existing_resource_dict[sub_attr], - user_provided_dict.get(sub_attr, None), - ) - ) - return False - - # If sub_attr not provided by user, check if the sub-attribute value of existing resource matches default value. - else: - if not should_dict_attr_be_excluded(option_name, sub_attr, exclude_list): - default_value_for_dict_attr = default_attribute_values.get( - option_name, None - ) - if default_value_for_dict_attr: - # if a default value for the sub-attr was provided by the module author, fail if the existing - # resource's value for the sub-attr is not the default - if not is_attr_assigned_default( - default_value_for_dict_attr, - sub_attr, - existing_resource_dict[sub_attr], - ): - return False - else: - # No default value specified by module author for sub_attr - _debug( - "Consider as match: Existing resource's attr {0} sub-attr {1} value is {2}, while user did" - "not provide a value for it. The module author also has not provided a default value for it" - "or marked it for exclusion. So ignoring this attribute during matching and continuing with" - "other checks".format( - option_name, sub_attr, existing_resource_dict[sub_attr] - ) - ) - - return True - - -def should_dict_attr_be_excluded(map_option_name, option_key, exclude_list): - """An entry for the Exclude list for excluding a map's key is specifed as a dict with the map option name as the - key, and the value as a list of keys to be excluded within that map. For example, if the keys "k1" and "k2" of a map - option named "m1" needs to be excluded, the exclude list must have an entry {'m1': ['k1','k2']} """ - for exclude_item in exclude_list: - if isinstance(exclude_item, dict): - if map_option_name in exclude_item: - if option_key in exclude_item[map_option_name]: - return True - return False - - -def create_and_wait( - resource_type, - client, - create_fn, - kwargs_create, - get_fn, - get_param, - module, - states=None, - wait_applicable=True, - kwargs_get=None, -): - """ - A utility function to create a resource and wait for the resource to get into the state as specified in the module - options. - :param wait_applicable: Specifies if wait for create is applicable for this resource - :param resource_type: Type of the resource to be created. e.g. "vcn" - :param client: OCI service client instance to call the service periodically to retrieve data. - e.g. VirtualNetworkClient() - :param create_fn: Function in the SDK to create the resource. e.g. virtual_network_client.create_vcn - :param kwargs_create: Dictionary containing arguments to be used to call the create function create_fn. - :param get_fn: Function in the SDK to get the resource. e.g. virtual_network_client.get_vcn - :param get_param: Name of the argument in the SDK get function. e.g. "vcn_id" - :param module: Instance of AnsibleModule. - :param states: List of lifecycle states to watch for while waiting after create_fn is called. - e.g. [module.params['wait_until'], "FAULTY"] - :param kwargs_get: Dictionary containing arguments to be used to call a multi-argument `get` function - :return: A dictionary containing the resource & the "changed" status. e.g. {"vcn":{x:y}, "changed":True} - """ - try: - return create_or_update_resource_and_wait( - resource_type, - create_fn, - kwargs_create, - module, - wait_applicable, - get_fn, - get_param, - states, - client, - kwargs_get, - ) - except MaximumWaitTimeExceeded as ex: - module.fail_json(msg=str(ex)) - except ServiceError as ex: - module.fail_json(msg=ex.message) - - -def update_and_wait( - resource_type, - client, - update_fn, - kwargs_update, - get_fn, - get_param, - module, - states=None, - wait_applicable=True, - kwargs_get=None, -): - """ - A utility function to update a resource and wait for the resource to get into the state as specified in the module - options. It wraps the create_and_wait method as apart from the method and arguments, everything else is similar. - :param wait_applicable: Specifies if wait for create is applicable for this resource - :param resource_type: Type of the resource to be created. e.g. "vcn" - :param client: OCI service client instance to call the service periodically to retrieve data. - e.g. VirtualNetworkClient() - :param update_fn: Function in the SDK to update the resource. e.g. virtual_network_client.update_vcn - :param kwargs_update: Dictionary containing arguments to be used to call the update function update_fn. - :param get_fn: Function in the SDK to get the resource. e.g. virtual_network_client.get_vcn - :param get_param: Name of the argument in the SDK get function. e.g. "vcn_id" - :param module: Instance of AnsibleModule. - :param kwargs_get: Dictionary containing arguments to be used to call the get function which requires multiple arguments. - :param states: List of lifecycle states to watch for while waiting after update_fn is called. - e.g. [module.params['wait_until'], "FAULTY"] - :return: A dictionary containing the resource & the "changed" status. e.g. {"vcn":{x:y}, "changed":True} - """ - try: - return create_or_update_resource_and_wait( - resource_type, - update_fn, - kwargs_update, - module, - wait_applicable, - get_fn, - get_param, - states, - client, - kwargs_get=kwargs_get, - ) - except MaximumWaitTimeExceeded as ex: - module.fail_json(msg=str(ex)) - except ServiceError as ex: - module.fail_json(msg=ex.message) - - -def create_or_update_resource_and_wait( - resource_type, - function, - kwargs_function, - module, - wait_applicable, - get_fn, - get_param, - states, - client, - update_target_resource_id_in_get_param=False, - kwargs_get=None, -): - """ - A utility function to create or update a resource and wait for the resource to get into the state as specified in - the module options. - :param resource_type: Type of the resource to be created. e.g. "vcn" - :param function: Function in the SDK to create or update the resource. - :param kwargs_function: Dictionary containing arguments to be used to call the create or update function - :param module: Instance of AnsibleModule. - :param wait_applicable: Specifies if wait for create is applicable for this resource - :param get_fn: Function in the SDK to get the resource. e.g. virtual_network_client.get_vcn - :param get_param: Name of the argument in the SDK get function. e.g. "vcn_id" - :param states: List of lifecycle states to watch for while waiting after create_fn is called. - e.g. [module.params['wait_until'], "FAULTY"] - :param client: OCI service client instance to call the service periodically to retrieve data. - e.g. VirtualNetworkClient() - :param kwargs_get: Dictionary containing arguments to be used to call the get function which requires multiple arguments. - :return: A dictionary containing the resource & the "changed" status. e.g. {"vcn":{x:y}, "changed":True} - """ - result = create_resource(resource_type, function, kwargs_function, module) - resource = result[resource_type] - result[resource_type] = wait_for_resource_lifecycle_state( - client, - module, - wait_applicable, - kwargs_get, - get_fn, - get_param, - resource, - states, - resource_type, - ) - return result - - -def wait_for_resource_lifecycle_state( - client, - module, - wait_applicable, - kwargs_get, - get_fn, - get_param, - resource, - states, - resource_type=None, -): - """ - A utility function to wait for the resource to get into the state as specified in - the module options. - :param client: OCI service client instance to call the service periodically to retrieve data. - e.g. VirtualNetworkClient - :param module: Instance of AnsibleModule. - :param wait_applicable: Specifies if wait for create is applicable for this resource - :param kwargs_get: Dictionary containing arguments to be used to call the get function which requires multiple arguments. - :param get_fn: Function in the SDK to get the resource. e.g. virtual_network_client.get_vcn - :param get_param: Name of the argument in the SDK get function. e.g. "vcn_id" - :param resource_type: Type of the resource to be created. e.g. "vcn" - :param states: List of lifecycle states to watch for while waiting after create_fn is called. - e.g. [module.params['wait_until'], "FAULTY"] - :return: A dictionary containing the resource & the "changed" status. e.g. {"vcn":{x:y}, "changed":True} - """ - if wait_applicable and module.params.get("wait", None): - if resource_type == "compartment": - # An immediate attempt to retrieve a compartment after a compartment is created fails with - # 'Authorization failed or requested resource not found', 'status': 404}. - # This is because it takes few seconds for the permissions on a compartment to be ready. - # Wait for few seconds before attempting a get call on compartment. - _debug( - "Pausing execution for permission on the newly created compartment to be ready." - ) - time.sleep(15) - if kwargs_get: - _debug( - "Waiting for resource to reach READY state. get_args: {0}".format( - kwargs_get - ) - ) - response_get = call_with_backoff(get_fn, **kwargs_get) - else: - _debug( - "Waiting for resource with id {0} to reach READY state.".format( - resource["id"] - ) - ) - response_get = call_with_backoff(get_fn, **{get_param: resource["id"]}) - if states is None: - states = module.params.get("wait_until") or DEFAULT_READY_STATES - resource = to_dict( - oci.wait_until( - client, - response_get, - evaluate_response=lambda r: r.data.lifecycle_state in states, - max_wait_seconds=module.params.get( - "wait_timeout", MAX_WAIT_TIMEOUT_IN_SECONDS - ), - ).data - ) - return resource - - -def wait_on_work_request(client, response, module): - try: - if module.params.get("wait", None): - _debug( - "Waiting for work request with id {0} to reach SUCCEEDED state.".format( - response.data.id - ) - ) - wait_response = oci.wait_until( - client, - response, - evaluate_response=lambda r: r.data.status == "SUCCEEDED", - max_wait_seconds=module.params.get( - "wait_timeout", MAX_WAIT_TIMEOUT_IN_SECONDS - ), - ) - else: - _debug( - "Waiting for work request with id {0} to reach ACCEPTED state.".format( - response.data.id - ) - ) - wait_response = oci.wait_until( - client, - response, - evaluate_response=lambda r: r.data.status == "ACCEPTED", - max_wait_seconds=module.params.get( - "wait_timeout", MAX_WAIT_TIMEOUT_IN_SECONDS - ), - ) - except MaximumWaitTimeExceeded as ex: - _debug(str(ex)) - module.fail_json(msg=str(ex)) - except ServiceError as ex: - _debug(str(ex)) - module.fail_json(msg=str(ex)) - return wait_response.data - - -def delete_and_wait( - resource_type, - client, - get_fn, - kwargs_get, - delete_fn, - kwargs_delete, - module, - states=None, - wait_applicable=True, - process_work_request=False, -): - """A utility function to delete a resource and wait for the resource to get into the state as specified in the - module options. - :param wait_applicable: Specifies if wait for delete is applicable for this resource - :param resource_type: Type of the resource to be deleted. e.g. "vcn" - :param client: OCI service client instance to call the service periodically to retrieve data. - e.g. VirtualNetworkClient() - :param get_fn: Function in the SDK to get the resource. e.g. virtual_network_client.get_vcn - :param kwargs_get: Dictionary of arguments for get function get_fn. e.g. {"vcn_id": module.params["id"]} - :param delete_fn: Function in the SDK to delete the resource. e.g. virtual_network_client.delete_vcn - :param kwargs_delete: Dictionary of arguments for delete function delete_fn. e.g. {"vcn_id": module.params["id"]} - :param module: Instance of AnsibleModule. - :param states: List of lifecycle states to watch for while waiting after delete_fn is called. If nothing is passed, - defaults to ["TERMINATED", "DETACHED", "DELETED"]. - :param process_work_request: Whether a work request is generated on an API call and if it needs to be handled. - :return: A dictionary containing the resource & the "changed" status. e.g. {"vcn":{x:y}, "changed":True} - """ - - states_set = set(["DETACHING", "DETACHED", "DELETING", "DELETED", "TERMINATING", "TERMINATED"]) - result = dict(changed=False) - result[resource_type] = dict() - try: - resource = to_dict(call_with_backoff(get_fn, **kwargs_get).data) - if resource: - if "lifecycle_state" not in resource or resource["lifecycle_state"] not in states_set: - response = call_with_backoff(delete_fn, **kwargs_delete) - if process_work_request: - wr_id = response.headers.get("opc-work-request-id") - get_wr_response = call_with_backoff( - client.get_work_request, work_request_id=wr_id - ) - result["work_request"] = to_dict( - wait_on_work_request(client, get_wr_response, module) - ) - # Set changed to True as work request has been created to delete the resource. - result["changed"] = True - resource = to_dict(call_with_backoff(get_fn, **kwargs_get).data) - else: - _debug("Deleted {0}, {1}".format(resource_type, resource)) - result["changed"] = True - - if wait_applicable and module.params.get("wait", None): - if states is None: - states = ( - module.params.get("wait_until") - or DEFAULT_TERMINATED_STATES - ) - try: - wait_response = oci.wait_until( - client, - get_fn(**kwargs_get), - evaluate_response=lambda r: r.data.lifecycle_state - in states, - max_wait_seconds=module.params.get( - "wait_timeout", MAX_WAIT_TIMEOUT_IN_SECONDS - ), - succeed_on_not_found=True, - ) - except MaximumWaitTimeExceeded as ex: - module.fail_json(msg=str(ex)) - except ServiceError as ex: - if ex.status != 404: - module.fail_json(msg=ex.message) - else: - # While waiting for resource to get into terminated state, if the resource is not found. - _debug( - "API returned Status:404(Not Found) while waiting for resource to get into" - " terminated state." - ) - resource["lifecycle_state"] = "DELETED" - result[resource_type] = resource - return result - # oci.wait_until() returns an instance of oci.util.Sentinel in case the resource is not found. - if type(wait_response) is not Sentinel: - resource = to_dict(wait_response.data) - else: - resource["lifecycle_state"] = "DELETED" - - result[resource_type] = resource - else: - _debug( - "Resource {0} with {1} already deleted. So returning changed=False".format( - resource_type, kwargs_get - ) - ) - except ServiceError as ex: - # DNS API throws a 400 InvalidParameter when a zone id is provided for zone_name_or_id and if the zone - # resource is not available, instead of the expected 404. So working around this for now. - if type(client) == oci.dns.DnsClient: - if ex.status == 400 and ex.code == "InvalidParameter": - _debug( - "Resource {0} with {1} already deleted. So returning changed=False".format( - resource_type, kwargs_get - ) - ) - elif ex.status != 404: - module.fail_json(msg=ex.message) - result[resource_type] = dict() - return result - - -def are_attrs_equal(current_resource, module, attributes): - """ - Check if the specified attributes are equal in the specified 'model' and 'module'. This is used to check if an OCI - Model instance already has the values specified by an Ansible user while invoking an OCI Ansible module and if a - resource needs to be updated. - :param current_resource: A resource model instance - :param module: The AnsibleModule representing the options provided by the user - :param attributes: A list of attributes that would need to be compared in the model and the module instances. - :return: True if the values for the list of attributes is the same in the model and module instances - """ - for attr in attributes: - curr_value = getattr(current_resource, attr, None) - user_provided_value = _get_user_provided_value(module, attribute_name=attr) - - if user_provided_value is not None: - if curr_value != user_provided_value: - _debug( - "are_attrs_equal - current resource's attribute " - + attr - + " value is " - + str(curr_value) - + " and this doesn't match user provided value of " - + str(user_provided_value) - ) - return False - return True - - -def _get_user_provided_value(module, attribute_name): - """ - Returns the user provided value for "attribute_name". We consider aliases in the module. - """ - user_provided_value = module.params.get(attribute_name, None) - if user_provided_value is None: - # If the attribute_name is set as an alias for some option X and user has provided value in the playbook using - # option X, then user provided value for attribute_name is equal to value for X. - # Get option name for attribute_name from module.aliases. - # module.aliases is a dictionary with key as alias name and its value as option name. - option_alias_for_attribute = module.aliases.get(attribute_name, None) - if option_alias_for_attribute is not None: - user_provided_value = module.params.get(option_alias_for_attribute, None) - return user_provided_value - - -def update_model_with_user_options(curr_model, update_model, module): - """ - Update the 'update_model' with user provided values in 'module' for the specified 'attributes' if they are different - from the values in the 'curr_model'. - :param curr_model: A resource model instance representing the state of the current resource - :param update_model: An instance of the update resource model for the current resource's type - :param module: An AnsibleModule representing the options provided by the user - :return: An updated 'update_model' instance filled with values that would need to be updated in the current resource - state to satisfy the user's requested state. - """ - attributes = update_model.attribute_map.keys() - for attr in attributes: - curr_value_for_attr = getattr(curr_model, attr, None) - user_provided_value = _get_user_provided_value(module, attribute_name=attr) - - if curr_value_for_attr != user_provided_value: - if user_provided_value is not None: - # Only update if a user has specified a value for an option - _debug( - "User requested {0} for attribute {1}, whereas the current value is {2}. So adding it " - "to the update model".format( - user_provided_value, attr, curr_value_for_attr - ) - ) - setattr(update_model, attr, user_provided_value) - else: - # Always set current values of the resource in the update model if there is no request for change in - # values - setattr(update_model, attr, curr_value_for_attr) - return update_model - - -def _get_retry_strategy(): - retry_strategy_builder = RetryStrategyBuilder( - max_attempts_check=True, - max_attempts=10, - retry_max_wait_between_calls_seconds=30, - retry_base_sleep_time_seconds=3, - backoff_type=oci.retry.BACKOFF_FULL_JITTER_EQUAL_ON_THROTTLE_VALUE, - ) - retry_strategy_builder.add_service_error_check( - service_error_retry_config={ - 429: [], - 400: ["QuotaExceeded", "LimitExceeded"], - 409: ["Conflict"], - }, - service_error_retry_on_any_5xx=True, - ) - return retry_strategy_builder.get_retry_strategy() - - -def call_with_backoff(fn, **kwargs): - if "retry_strategy" not in kwargs: - kwargs["retry_strategy"] = _get_retry_strategy() - try: - return fn(**kwargs) - except TypeError as te: - if "unexpected keyword argument" in str(te): - # to handle older SDKs that did not support retry_strategy - del kwargs["retry_strategy"] - return fn(**kwargs) - else: - # A validation error raised by the SDK, throw it back - raise - - -def generic_hash(obj): - """ - Compute a hash of all the fields in the object - :param obj: Object whose hash needs to be computed - :return: a hash value for the object - """ - sum = 0 - for field in obj.attribute_map.keys(): - field_value = getattr(obj, field) - if isinstance(field_value, list): - for value in field_value: - sum = sum + hash(value) - elif isinstance(field_value, dict): - for k, v in field_value.items(): - sum = sum + hash(hash(k) + hash(":") + hash(v)) - else: - sum = sum + hash(getattr(obj, field)) - return sum - - -def generic_eq(s, other): - if other is None: - return False - return s.__dict__ == other.__dict__ - - -def generate_subclass(parent_class): - """Make a class hash-able by generating a subclass with a __hash__ method that returns the sum of all fields within - the parent class""" - dict_of_method_in_subclass = { - "__init__": parent_class.__init__, - "__hash__": generic_hash, - "__eq__": generic_eq, - } - subclass_name = "GeneratedSub" + parent_class.__name__ - generated_sub_class = type( - subclass_name, (parent_class,), dict_of_method_in_subclass - ) - return generated_sub_class - - -def create_hashed_instance(class_type): - hashed_class = generate_subclass(class_type) - return hashed_class() - - -def get_hashed_object_list(class_type, object_with_values, attributes_class_type=None): - if object_with_values is None: - return None - hashed_class_instances = [] - for object_with_value in object_with_values: - hashed_class_instances.append( - get_hashed_object(class_type, object_with_value, attributes_class_type) - ) - return hashed_class_instances - - -def get_hashed_object( - class_type, object_with_value, attributes_class_type=None, supported_attributes=None -): - """ - Convert any class instance into hashable so that the - instances are eligible for various comparison - operation available under set() object. - :param class_type: Any class type whose instances needs to be hashable - :param object_with_value: Instance of the class type with values which - would be set in the resulting isinstance - :param attributes_class_type: A list of class types of attributes, if attribute is a custom class instance - :param supported_attributes: A list of attributes which should be considered while populating the instance - with the values in the object. This helps in avoiding new attributes of the class_type which are still not - supported by the current implementation. - :return: A hashable instance with same state of the provided object_with_value - """ - if object_with_value is None: - return None - - HashedClass = generate_subclass(class_type) - hashed_class_instance = HashedClass() - - if supported_attributes: - class_attributes = list( - set(hashed_class_instance.attribute_map) & set(supported_attributes) - ) - else: - class_attributes = hashed_class_instance.attribute_map - - for attribute in class_attributes: - attribute_value = getattr(object_with_value, attribute) - if attributes_class_type: - for attribute_class_type in attributes_class_type: - if isinstance(attribute_value, attribute_class_type): - attribute_value = get_hashed_object( - attribute_class_type, attribute_value - ) - hashed_class_instance.__setattr__(attribute, attribute_value) - - return hashed_class_instance - - -def update_class_type_attr_difference( - update_class_details, existing_instance, attr_name, attr_class, input_attr_value -): - """ - Checks the difference and updates an attribute which is represented by a class - instance. Not aplicable if the attribute type is a primitive value. - For example, if a class name is A with an attribute x, then if A.x = X(), then only - this method works. - :param update_class_details The instance which should be updated if there is change in - attribute value - :param existing_instance The instance whose attribute value is compared with input - attribute value - :param attr_name Name of the attribute whose value should be compared - :param attr_class Class type of the attribute - :param input_attr_value The value of input attribute which should replaced the current - value in case of mismatch - :return: A boolean value indicating whether attribute value has been replaced - """ - changed = False - # Here existing attribute values is an instance - existing_attr_value = get_hashed_object( - attr_class, getattr(existing_instance, attr_name) - ) - if input_attr_value is None: - update_class_details.__setattr__(attr_name, existing_attr_value) - else: - changed = not input_attr_value.__eq__(existing_attr_value) - if changed: - update_class_details.__setattr__(attr_name, input_attr_value) - else: - update_class_details.__setattr__(attr_name, existing_attr_value) - - return changed - - -def get_existing_resource(target_fn, module, **kwargs): - """ - Returns the requested resource if it exists based on the input arguments. - :param target_fn The function which should be used to find the requested resource - :param module Instance of AnsibleModule attribute value - :param kwargs A map of arguments consisting of values based on which requested resource should be searched - :return: Instance of requested resource - """ - existing_resource = None - try: - response = call_with_backoff(target_fn, **kwargs) - existing_resource = response.data - except ServiceError as ex: - if ex.status != 404: - module.fail_json(msg=ex.message) - - return existing_resource - - -def get_attached_instance_info( - module, lookup_attached_instance, list_attachments_fn, list_attachments_args -): - config = get_oci_config(module) - identity_client = create_service_client(module, IdentityClient) - - volume_attachments = [] - - if lookup_attached_instance: - # Get all the compartments in the tenancy - compartments = to_dict( - identity_client.list_compartments( - config.get("tenancy"), compartment_id_in_subtree=True - ).data - ) - # For each compartment, get the volume attachments for the compartment_id with the other args in - # list_attachments_args. - for compartment in compartments: - list_attachments_args["compartment_id"] = compartment["id"] - try: - volume_attachments += list_all_resources( - list_attachments_fn, **list_attachments_args - ) - - # Pass ServiceError due to authorization issue in accessing volume attachments of a compartment - except ServiceError as ex: - if ex.status == 404: - pass - - else: - volume_attachments = list_all_resources( - list_attachments_fn, **list_attachments_args - ) - - volume_attachments = to_dict(volume_attachments) - # volume_attachments has attachments in DETACHING or DETACHED state. Return the volume attachment in ATTACHING or - # ATTACHED state - - return next( - ( - volume_attachment - for volume_attachment in volume_attachments - if volume_attachment["lifecycle_state"] in ["ATTACHING", "ATTACHED"] - ), - None, - ) - - -def check_mode(fn): - def wrapper(*args, **kwargs): - if os.environ.get("OCI_ANSIBLE_EXPERIMENTAL", None): - return fn(*args, **kwargs) - return None - - return wrapper - - -def check_and_return_component_list_difference( - input_component_list, existing_components, purge_components, delete_components=False -): - if input_component_list: - existing_components, changed = get_component_list_difference( - input_component_list, - existing_components, - purge_components, - delete_components, - ) - else: - existing_components = [] - changed = True - return existing_components, changed - - -def get_component_list_difference( - input_component_list, existing_components, purge_components, delete_components=False -): - if delete_components: - if existing_components is None: - return None, False - component_differences = set(existing_components).intersection( - set(input_component_list) - ) - if component_differences: - return list(set(existing_components) - component_differences), True - else: - return None, False - if existing_components is None: - return input_component_list, True - if purge_components: - components_differences = set(input_component_list).symmetric_difference( - set(existing_components) - ) - - if components_differences: - return input_component_list, True - - components_differences = set(input_component_list).difference( - set(existing_components) - ) - if components_differences: - return list(components_differences) + existing_components, True - return None, False - - -def write_to_file(path, content): - with open(to_bytes(path), "wb") as dest_file: - dest_file.write(content) - - -def get_target_resource_from_list( - module, list_resource_fn, target_resource_id=None, **kwargs -): - """ - Returns a resource filtered by identifer from a list of resources. This method should be - used as an alternative of 'get resource' method when 'get resource' is nor provided by - resource api. This method returns a wrapper of response object but that should not be - used as an input to 'wait_until' utility as this is only a partial wrapper of response object. - :param module The AnsibleModule representing the options provided by the user - :param list_resource_fn The function which lists all the resources - :param target_resource_id The identifier of the resource which should be filtered from the list - :param kwargs A map of arguments consisting of values based on which requested resource should be searched - :return: A custom wrapper which partially wraps a response object where the data field contains the target - resource, if found. - """ - - class ResponseWrapper: - def __init__(self, data): - self.data = data - - try: - resources = list_all_resources(list_resource_fn, **kwargs) - if resources is not None: - for resource in resources: - if resource.id == target_resource_id: - # Returning an object that mimics an OCI response as oci_utils methods assumes an Response-ish - # object - return ResponseWrapper(data=resource) - return ResponseWrapper(data=None) - except ServiceError as ex: - module.fail_json(msg=ex.message) diff --git a/lib/ansible/modules/cloud/oracle/__init__.py b/lib/ansible/modules/cloud/oracle/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/lib/ansible/modules/cloud/oracle/oci_vcn.py b/lib/ansible/modules/cloud/oracle/oci_vcn.py deleted file mode 100644 index 07a62f47377..00000000000 --- a/lib/ansible/modules/cloud/oracle/oci_vcn.py +++ /dev/null @@ -1,225 +0,0 @@ -#!/usr/bin/python -# Copyright (c) 2017, 2018, Oracle and/or its affiliates. -# This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# Apache License v2.0 -# See LICENSE.TXT for details. - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "community", -} - -DOCUMENTATION = """ ---- -module: oci_vcn -short_description: Manage Virtual Cloud Networks(VCN) in OCI -description: - - This module allows the user to create, delete and update virtual cloud networks(VCNs) in OCI. - The complete Oracle Cloud Infrastructure Ansible Modules can be downloaded from - U(https://github.com/oracle/oci-ansible-modules/releases). -version_added: "2.8" -options: - cidr_block: - description: The CIDR IP address block of the VCN. Required when creating a VCN with I(state=present). - required: false - compartment_id: - description: The OCID of the compartment to contain the VCN. Required when creating a VCN with I(state=present). - This option is mutually exclusive with I(vcn_id). - type: str - display_name: - description: A user-friendly name. Does not have to be unique, and it's changeable. - type: str - aliases: [ 'name' ] - dns_label: - description: A DNS label for the VCN, used in conjunction with the VNIC's hostname and subnet's DNS label to - form a fully qualified domain name (FQDN) for each VNIC within this subnet (for example, - bminstance-1.subnet123.vcn1.oraclevcn.com). Not required to be unique, but it's a best practice - to set unique DNS labels for VCNs in your tenancy. Must be an alphanumeric string that begins - with a letter. The value cannot be changed. - type: str - state: - description: Create or update a VCN with I(state=present). Use I(state=absent) to delete a VCN. - type: str - default: present - choices: ['present', 'absent'] - vcn_id: - description: The OCID of the VCN. Required when deleting a VCN with I(state=absent) or updating a VCN - with I(state=present). This option is mutually exclusive with I(compartment_id). - type: str - aliases: [ 'id' ] -author: "Rohit Chaware (@rohitChaware)" -extends_documentation_fragment: [ oracle, oracle_creatable_resource, oracle_wait_options, oracle_tags ] -""" - -EXAMPLES = """ -- name: Create a VCN - oci_vcn: - cidr_block: '10.0.0.0/16' - compartment_id: 'ocid1.compartment.oc1..xxxxxEXAMPLExxxxx' - display_name: my_vcn - dns_label: ansiblevcn - -- name: Updates the specified VCN's display name - oci_vcn: - vcn_id: ocid1.vcn.oc1.phx.xxxxxEXAMPLExxxxx - display_name: ansible_vcn - -- name: Delete the specified VCN - oci_vcn: - vcn_id: ocid1.vcn.oc1.phx.xxxxxEXAMPLExxxxx - state: absent -""" - -RETURN = """ -vcn: - description: Information about the VCN - returned: On successful create and update operation - type: dict - sample: { - "cidr_block": "10.0.0.0/16", - compartment_id": "ocid1.compartment.oc1..xxxxxEXAMPLExxxxx", - "default_dhcp_options_id": "ocid1.dhcpoptions.oc1.phx.xxxxxEXAMPLExxxxx", - "default_route_table_id": "ocid1.routetable.oc1.phx.xxxxxEXAMPLExxxxx", - "default_security_list_id": "ocid1.securitylist.oc1.phx.xxxxxEXAMPLExxxxx", - "display_name": "ansible_vcn", - "dns_label": "ansiblevcn", - "id": "ocid1.vcn.oc1.phx.xxxxxEXAMPLExxxxx", - "lifecycle_state": "AVAILABLE", - "time_created": "2017-11-13T20:22:40.626000+00:00", - "vcn_domain_name": "ansiblevcn.oraclevcn.com" - } -""" - -from ansible.module_utils.basic import AnsibleModule, missing_required_lib -from ansible.module_utils.oracle import oci_utils - -try: - from oci.core.virtual_network_client import VirtualNetworkClient - from oci.core.models import CreateVcnDetails - from oci.core.models import UpdateVcnDetails - - HAS_OCI_PY_SDK = True -except ImportError: - HAS_OCI_PY_SDK = False - - -def delete_vcn(virtual_network_client, module): - result = oci_utils.delete_and_wait( - resource_type="vcn", - client=virtual_network_client, - get_fn=virtual_network_client.get_vcn, - kwargs_get={"vcn_id": module.params["vcn_id"]}, - delete_fn=virtual_network_client.delete_vcn, - kwargs_delete={"vcn_id": module.params["vcn_id"]}, - module=module, - ) - return result - - -def update_vcn(virtual_network_client, module): - result = oci_utils.check_and_update_resource( - resource_type="vcn", - client=virtual_network_client, - get_fn=virtual_network_client.get_vcn, - kwargs_get={"vcn_id": module.params["vcn_id"]}, - update_fn=virtual_network_client.update_vcn, - primitive_params_update=["vcn_id"], - kwargs_non_primitive_update={UpdateVcnDetails: "update_vcn_details"}, - module=module, - update_attributes=UpdateVcnDetails().attribute_map.keys(), - ) - return result - - -def create_vcn(virtual_network_client, module): - create_vcn_details = CreateVcnDetails() - for attribute in create_vcn_details.attribute_map.keys(): - if attribute in module.params: - setattr(create_vcn_details, attribute, module.params[attribute]) - - result = oci_utils.create_and_wait( - resource_type="vcn", - create_fn=virtual_network_client.create_vcn, - kwargs_create={"create_vcn_details": create_vcn_details}, - client=virtual_network_client, - get_fn=virtual_network_client.get_vcn, - get_param="vcn_id", - module=module, - ) - return result - - -def main(): - module_args = oci_utils.get_taggable_arg_spec( - supports_create=True, supports_wait=True - ) - module_args.update( - dict( - cidr_block=dict(type="str", required=False), - compartment_id=dict(type="str", required=False), - display_name=dict(type="str", required=False, aliases=["name"]), - dns_label=dict(type="str", required=False), - state=dict( - type="str", - required=False, - default="present", - choices=["absent", "present"], - ), - vcn_id=dict(type="str", required=False, aliases=["id"]), - ) - ) - - module = AnsibleModule( - argument_spec=module_args, - supports_check_mode=False, - mutually_exclusive=[["compartment_id", "vcn_id"]], - ) - - if not HAS_OCI_PY_SDK: - module.fail_json(msg=missing_required_lib("oci")) - - virtual_network_client = oci_utils.create_service_client( - module, VirtualNetworkClient - ) - - exclude_attributes = {"display_name": True, "dns_label": True} - state = module.params["state"] - vcn_id = module.params["vcn_id"] - - if state == "absent": - if vcn_id is not None: - result = delete_vcn(virtual_network_client, module) - else: - module.fail_json( - msg="Specify vcn_id with state as 'absent' to delete a VCN." - ) - - else: - if vcn_id is not None: - result = update_vcn(virtual_network_client, module) - else: - result = oci_utils.check_and_create_resource( - resource_type="vcn", - create_fn=create_vcn, - kwargs_create={ - "virtual_network_client": virtual_network_client, - "module": module, - }, - list_fn=virtual_network_client.list_vcns, - kwargs_list={"compartment_id": module.params["compartment_id"]}, - module=module, - model=CreateVcnDetails(), - exclude_attributes=exclude_attributes, - ) - - module.exit_json(**result) - - -if __name__ == "__main__": - main() diff --git a/lib/ansible/plugins/doc_fragments/oracle.py b/lib/ansible/plugins/doc_fragments/oracle.py deleted file mode 100644 index 0c1075e55f2..00000000000 --- a/lib/ansible/plugins/doc_fragments/oracle.py +++ /dev/null @@ -1,75 +0,0 @@ -class ModuleDocFragment(object): - DOCUMENTATION = """ - requirements: - - "python >= 2.7" - - Python SDK for Oracle Cloud Infrastructure U(https://oracle-cloud-infrastructure-python-sdk.readthedocs.io) - notes: - - For OCI python sdk configuration, please refer to - U(https://oracle-cloud-infrastructure-python-sdk.readthedocs.io/en/latest/configuration.html) - options: - config_file_location: - description: - - Path to configuration file. If not set then the value of the OCI_CONFIG_FILE environment variable, - if any, is used. Otherwise, defaults to ~/.oci/config. - type: str - config_profile_name: - description: - - The profile to load from the config file referenced by C(config_file_location). If not set, then the - value of the OCI_CONFIG_PROFILE environment variable, if any, is used. Otherwise, defaults to the - "DEFAULT" profile in C(config_file_location). - default: "DEFAULT" - type: str - api_user: - description: - - The OCID of the user, on whose behalf, OCI APIs are invoked. If not set, then the - value of the OCI_USER_OCID environment variable, if any, is used. This option is required if the user - is not specified through a configuration file (See C(config_file_location)). To get the user's OCID, - please refer U(https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/apisigningkey.htm). - type: str - api_user_fingerprint: - description: - - Fingerprint for the key pair being used. If not set, then the value of the OCI_USER_FINGERPRINT - environment variable, if any, is used. This option is required if the key fingerprint is not - specified through a configuration file (See C(config_file_location)). To get the key pair's - fingerprint value please refer - U(https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/apisigningkey.htm). - type: str - api_user_key_file: - description: - - Full path and filename of the private key (in PEM format). If not set, then the value of the - OCI_USER_KEY_FILE variable, if any, is used. This option is required if the private key is - not specified through a configuration file (See C(config_file_location)). If the key is encrypted - with a pass-phrase, the C(api_user_key_pass_phrase) option must also be provided. - type: str - api_user_key_pass_phrase: - description: - - Passphrase used by the key referenced in C(api_user_key_file), if it is encrypted. If not set, then - the value of the OCI_USER_KEY_PASS_PHRASE variable, if any, is used. This option is required if the - key passphrase is not specified through a configuration file (See C(config_file_location)). - type: str - auth_type: - description: - - The type of authentication to use for making API requests. By default C(auth_type="api_key") based - authentication is performed and the API key (see I(api_user_key_file)) in your config file will be - used. If this 'auth_type' module option is not specified, the value of the OCI_ANSIBLE_AUTH_TYPE, - if any, is used. Use C(auth_type="instance_principal") to use instance principal based authentication - when running ansible playbooks within an OCI compute instance. - choices: ['api_key', 'instance_principal'] - default: 'api_key' - type: str - tenancy: - description: - - OCID of your tenancy. If not set, then the value of the OCI_TENANCY variable, if any, is - used. This option is required if the tenancy OCID is not specified through a configuration file - (See C(config_file_location)). To get the tenancy OCID, please refer - U(https://docs.us-phoenix-1.oraclecloud.com/Content/API/Concepts/apisigningkey.htm) - type: str - region: - description: - - The Oracle Cloud Infrastructure region to use for all OCI API requests. If not set, then the - value of the OCI_REGION variable, if any, is used. This option is required if the region is - not specified through a configuration file (See C(config_file_location)). Please refer to - U(https://docs.us-phoenix-1.oraclecloud.com/Content/General/Concepts/regions.htm) for more information - on OCI regions. - type: str - """ diff --git a/lib/ansible/plugins/doc_fragments/oracle_creatable_resource.py b/lib/ansible/plugins/doc_fragments/oracle_creatable_resource.py deleted file mode 100644 index 7f61ca4f432..00000000000 --- a/lib/ansible/plugins/doc_fragments/oracle_creatable_resource.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2018, Oracle and/or its affiliates. -# This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# Apache License v2.0 -# See LICENSE.TXT for details. - - -class ModuleDocFragment(object): - DOCUMENTATION = """ - options: - force_create: - description: Whether to attempt non-idempotent creation of a resource. By default, create resource is an - idempotent operation, and doesn't create the resource if it already exists. Setting this option - to true, forcefully creates a copy of the resource, even if it already exists.This option is - mutually exclusive with I(key_by). - default: False - type: bool - key_by: - description: The list of comma-separated attributes of this resource which should be used to uniquely - identify an instance of the resource. By default, all the attributes of a resource except - I(freeform_tags) are used to uniquely identify a resource. - type: list - """ diff --git a/lib/ansible/plugins/doc_fragments/oracle_display_name_option.py b/lib/ansible/plugins/doc_fragments/oracle_display_name_option.py deleted file mode 100644 index fd1eb7f2508..00000000000 --- a/lib/ansible/plugins/doc_fragments/oracle_display_name_option.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2018, Oracle and/or its affiliates. -# This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# Apache License v2.0 -# See LICENSE.TXT for details. - - -class ModuleDocFragment(object): - DOCUMENTATION = """ - options: - display_name: - description: Use I(display_name) along with the other options to return only resources that match the given - display name exactly. - type: str - """ diff --git a/lib/ansible/plugins/doc_fragments/oracle_name_option.py b/lib/ansible/plugins/doc_fragments/oracle_name_option.py deleted file mode 100644 index 26d5f4f1605..00000000000 --- a/lib/ansible/plugins/doc_fragments/oracle_name_option.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2018, Oracle and/or its affiliates. -# This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# Apache License v2.0 -# See LICENSE.TXT for details. - - -class ModuleDocFragment(object): - DOCUMENTATION = """ - options: - name: - description: Use I(name) along with the other options to return only resources that match the given name - exactly. - type: str - """ diff --git a/lib/ansible/plugins/doc_fragments/oracle_tags.py b/lib/ansible/plugins/doc_fragments/oracle_tags.py deleted file mode 100644 index 0d7b016e760..00000000000 --- a/lib/ansible/plugins/doc_fragments/oracle_tags.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2018, Oracle and/or its affiliates. -# This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# Apache License v2.0 -# See LICENSE.TXT for details. - - -class ModuleDocFragment(object): - DOCUMENTATION = """ - options: - defined_tags: - description: Defined tags for this resource. Each key is predefined and scoped to a namespace. For more - information, see - U(https://docs.us-phoenix-1.oraclecloud.com/Content/General/Concepts/resourcetags.htm). - type: dict - freeform_tags: - description: Free-form tags for this resource. Each tag is a simple key-value pair with no predefined name, - type, or namespace. For more information, see - U(https://docs.us-phoenix-1.oraclecloud.com/Content/General/Concepts/resourcetags.htm). - type: dict - """ diff --git a/lib/ansible/plugins/doc_fragments/oracle_wait_options.py b/lib/ansible/plugins/doc_fragments/oracle_wait_options.py deleted file mode 100644 index 98693d9779f..00000000000 --- a/lib/ansible/plugins/doc_fragments/oracle_wait_options.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) 2018, Oracle and/or its affiliates. -# This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -# Apache License v2.0 -# See LICENSE.TXT for details. - - -class ModuleDocFragment(object): - DOCUMENTATION = """ - options: - wait: - description: Whether to wait for create or delete operation to complete. - default: yes - type: bool - wait_timeout: - description: Time, in seconds, to wait when I(wait=yes). - default: 1200 - type: int - wait_until: - description: The lifecycle state to wait for the resource to transition into when I(wait=yes). By default, - when I(wait=yes), we wait for the resource to get into ACTIVE/ATTACHED/AVAILABLE/PROVISIONED/ - RUNNING applicable lifecycle state during create operation & to get into DELETED/DETACHED/ - TERMINATED lifecycle state during delete operation. - type: str - """