From 6f4aed53772f2b5ff83262575865f2be5b36933b Mon Sep 17 00:00:00 2001 From: Matt Martz Date: Wed, 30 Sep 2020 08:56:27 -0500 Subject: [PATCH] Remove incidental_nios_txt_record (#72009) * Add explicit coverage of argspec type=dict * Non string mapping failure * ci_complete ci_coverage * Remove incidental_nios_txt_record and associated files * Don't forget the ignore.txt changes * ci_complete ci_coverage --- .../targets/argspec/library/argspec.py | 4 +- .../targets/argspec/tasks/main.yml | 42 ++ .../incidental_nios_prepare_tests/aliases | 1 - .../tasks/main.yml | 0 .../incidental_nios_txt_record/aliases | 3 - .../defaults/main.yaml | 3 - .../incidental_nios_txt_record/meta/main.yaml | 2 - .../incidental_nios_txt_record/tasks/main.yml | 1 - .../tasks/nios_txt_record_idempotence.yml | 80 --- test/sanity/ignore.txt | 2 - .../module_utils/net_tools/nios/__init__.py | 0 .../module_utils/net_tools/nios/api.py | 601 ------------------ .../plugins/modules/nios_txt_record.py | 134 ---- .../integration/plugins/modules/nios_zone.py | 228 ------- 14 files changed, 45 insertions(+), 1056 deletions(-) delete mode 100644 test/integration/targets/incidental_nios_prepare_tests/aliases delete mode 100644 test/integration/targets/incidental_nios_prepare_tests/tasks/main.yml delete mode 100644 test/integration/targets/incidental_nios_txt_record/aliases delete mode 100644 test/integration/targets/incidental_nios_txt_record/defaults/main.yaml delete mode 100644 test/integration/targets/incidental_nios_txt_record/meta/main.yaml delete mode 100644 test/integration/targets/incidental_nios_txt_record/tasks/main.yml delete mode 100644 test/integration/targets/incidental_nios_txt_record/tasks/nios_txt_record_idempotence.yml delete mode 100644 test/support/integration/plugins/module_utils/net_tools/nios/__init__.py delete mode 100644 test/support/integration/plugins/module_utils/net_tools/nios/api.py delete mode 100644 test/support/integration/plugins/modules/nios_txt_record.py delete mode 100644 test/support/integration/plugins/modules/nios_zone.py diff --git a/test/integration/targets/argspec/library/argspec.py b/test/integration/targets/argspec/library/argspec.py index d117b5c99b6..3cfdf925e63 100644 --- a/test/integration/targets/argspec/library/argspec.py +++ b/test/integration/targets/argspec/library/argspec.py @@ -12,12 +12,14 @@ def main(): module = AnsibleModule( { 'state': { - 'default': 'present', 'type': 'str', 'choices': ['absent', 'present'], }, 'path': {}, 'content': {}, + 'mapping': { + 'type': 'dict', + }, }, required_if=( ('state', 'present', ('path', 'content'), True), diff --git a/test/integration/targets/argspec/tasks/main.yml b/test/integration/targets/argspec/tasks/main.yml index 4aaf1862433..d4312cc2857 100644 --- a/test/integration/targets/argspec/tasks/main.yml +++ b/test/integration/targets/argspec/tasks/main.yml @@ -24,6 +24,36 @@ register: argspec_mutually_exclusive_fail ignore_errors: true +- argspec: + mapping: + foo: bar + register: argspec_good_mapping + +- argspec: + mapping: foo=bar + register: argspec_good_mapping_kv + +- argspec: + mapping: !!str '{"foo": "bar"}' + register: argspec_good_mapping_json + +- argspec: + mapping: foo + register: argspec_bad_mapping_string + ignore_errors: true + +- argspec: + mapping: 1 + register: argspec_bad_mapping_int + ignore_errors: true + +- argspec: + mapping: + - foo + - bar + register: argspec_bad_mapping_list + ignore_errors: true + - assert: that: - argspec_simple_good is successful @@ -31,3 +61,15 @@ - argspec_required_if_good1 is successful - argspec_required_if_good2 is successful - argspec_mutually_exclusive_fail is failed + - argspec_good_mapping is successful + - >- + argspec_good_mapping.mapping == {'foo': 'bar'} + - argspec_good_mapping_json is successful + - >- + argspec_good_mapping_json.mapping == {'foo': 'bar'} + - argspec_good_mapping_kv is successful + - >- + argspec_good_mapping_kv.mapping == {'foo': 'bar'} + - argspec_bad_mapping_string is failed + - argspec_bad_mapping_int is failed + - argspec_bad_mapping_list is failed diff --git a/test/integration/targets/incidental_nios_prepare_tests/aliases b/test/integration/targets/incidental_nios_prepare_tests/aliases deleted file mode 100644 index 136c05e0d02..00000000000 --- a/test/integration/targets/incidental_nios_prepare_tests/aliases +++ /dev/null @@ -1 +0,0 @@ -hidden diff --git a/test/integration/targets/incidental_nios_prepare_tests/tasks/main.yml b/test/integration/targets/incidental_nios_prepare_tests/tasks/main.yml deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test/integration/targets/incidental_nios_txt_record/aliases b/test/integration/targets/incidental_nios_txt_record/aliases deleted file mode 100644 index dfb77b81527..00000000000 --- a/test/integration/targets/incidental_nios_txt_record/aliases +++ /dev/null @@ -1,3 +0,0 @@ -shippable/cloud/incidental -cloud/nios -destructive diff --git a/test/integration/targets/incidental_nios_txt_record/defaults/main.yaml b/test/integration/targets/incidental_nios_txt_record/defaults/main.yaml deleted file mode 100644 index ebf6ffc9035..00000000000 --- a/test/integration/targets/incidental_nios_txt_record/defaults/main.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -testcase: "*" -test_items: [] \ No newline at end of file diff --git a/test/integration/targets/incidental_nios_txt_record/meta/main.yaml b/test/integration/targets/incidental_nios_txt_record/meta/main.yaml deleted file mode 100644 index c7c538f4e75..00000000000 --- a/test/integration/targets/incidental_nios_txt_record/meta/main.yaml +++ /dev/null @@ -1,2 +0,0 @@ -dependencies: - - incidental_nios_prepare_tests diff --git a/test/integration/targets/incidental_nios_txt_record/tasks/main.yml b/test/integration/targets/incidental_nios_txt_record/tasks/main.yml deleted file mode 100644 index e15b4c55dbc..00000000000 --- a/test/integration/targets/incidental_nios_txt_record/tasks/main.yml +++ /dev/null @@ -1 +0,0 @@ -- include: nios_txt_record_idempotence.yml diff --git a/test/integration/targets/incidental_nios_txt_record/tasks/nios_txt_record_idempotence.yml b/test/integration/targets/incidental_nios_txt_record/tasks/nios_txt_record_idempotence.yml deleted file mode 100644 index 3b7357afafe..00000000000 --- a/test/integration/targets/incidental_nios_txt_record/tasks/nios_txt_record_idempotence.yml +++ /dev/null @@ -1,80 +0,0 @@ -- name: cleanup the parent object - nios_zone: - name: ansible.com - state: absent - provider: "{{ nios_provider }}" - -- name: create the parent object - nios_zone: - name: ansible.com - state: present - provider: "{{ nios_provider }}" - -- name: cleanup txt record - nios_txt_record: - name: txt.ansible.com - text: mytext - state: absent - provider: "{{ nios_provider }}" - -- name: create txt record - nios_txt_record: - name: txt.ansible.com - text: mytext - state: present - provider: "{{ nios_provider }}" - register: txt_create1 - -- name: create txt record - nios_txt_record: - name: txt.ansible.com - text: mytext - state: present - provider: "{{ nios_provider }}" - register: txt_create2 - -- assert: - that: - - "txt_create1.changed" - - "not txt_create2.changed" - -- name: add a comment to an existing txt record - nios_txt_record: - name: txt.ansible.com - text: mytext - state: present - comment: mycomment - provider: "{{ nios_provider }}" - register: txt_update1 - -- name: add a comment to an existing txt record - nios_txt_record: - name: txt.ansible.com - text: mytext - state: present - comment: mycomment - provider: "{{ nios_provider }}" - register: txt_update2 - -- name: remove a txt record from the system - nios_txt_record: - name: txt.ansible.com - state: absent - provider: "{{ nios_provider }}" - register: txt_delete1 - -- name: remove a txt record from the system - nios_txt_record: - name: txt.ansible.com - state: absent - provider: "{{ nios_provider }}" - register: txt_delete2 - -- assert: - that: - - "txt_create1.changed" - - "not txt_create2.changed" - - "txt_update1.changed" - - "not txt_update2.changed" - - "txt_delete1.changed" - - "not txt_delete2.changed" diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index 47dbaf19d5b..58a4123eef0 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -208,8 +208,6 @@ test/support/integration/plugins/module_utils/k8s/common.py metaclass-boilerplat test/support/integration/plugins/module_utils/k8s/raw.py metaclass-boilerplate test/support/integration/plugins/module_utils/mysql.py future-import-boilerplate test/support/integration/plugins/module_utils/mysql.py metaclass-boilerplate -test/support/integration/plugins/module_utils/net_tools/nios/api.py future-import-boilerplate -test/support/integration/plugins/module_utils/net_tools/nios/api.py metaclass-boilerplate test/support/integration/plugins/module_utils/network/common/utils.py future-import-boilerplate test/support/integration/plugins/module_utils/network/common/utils.py metaclass-boilerplate test/support/integration/plugins/module_utils/postgres.py future-import-boilerplate diff --git a/test/support/integration/plugins/module_utils/net_tools/nios/__init__.py b/test/support/integration/plugins/module_utils/net_tools/nios/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/test/support/integration/plugins/module_utils/net_tools/nios/api.py b/test/support/integration/plugins/module_utils/net_tools/nios/api.py deleted file mode 100644 index 2a759033e2c..00000000000 --- a/test/support/integration/plugins/module_utils/net_tools/nios/api.py +++ /dev/null @@ -1,601 +0,0 @@ -# This code is part of Ansible, but is an independent component. -# This particular file snippet, and this file snippet only, is BSD licensed. -# Modules you write using this snippet, which is embedded dynamically by Ansible -# still belong to the author of the module, and may assign their own license -# to the complete work. -# -# (c) 2018 Red Hat Inc. -# -# Redistribution and use in source and binary forms, with or without modification, -# are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -# USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -import os -from functools import partial -from ansible.module_utils._text import to_native -from ansible.module_utils.six import iteritems -from ansible.module_utils._text import to_text -from ansible.module_utils.basic import env_fallback - -try: - from infoblox_client.connector import Connector - from infoblox_client.exceptions import InfobloxException - HAS_INFOBLOX_CLIENT = True -except ImportError: - HAS_INFOBLOX_CLIENT = False - -# defining nios constants -NIOS_DNS_VIEW = 'view' -NIOS_NETWORK_VIEW = 'networkview' -NIOS_HOST_RECORD = 'record:host' -NIOS_IPV4_NETWORK = 'network' -NIOS_IPV6_NETWORK = 'ipv6network' -NIOS_ZONE = 'zone_auth' -NIOS_PTR_RECORD = 'record:ptr' -NIOS_A_RECORD = 'record:a' -NIOS_AAAA_RECORD = 'record:aaaa' -NIOS_CNAME_RECORD = 'record:cname' -NIOS_MX_RECORD = 'record:mx' -NIOS_SRV_RECORD = 'record:srv' -NIOS_NAPTR_RECORD = 'record:naptr' -NIOS_TXT_RECORD = 'record:txt' -NIOS_NSGROUP = 'nsgroup' -NIOS_IPV4_FIXED_ADDRESS = 'fixedaddress' -NIOS_IPV6_FIXED_ADDRESS = 'ipv6fixedaddress' -NIOS_NEXT_AVAILABLE_IP = 'func:nextavailableip' -NIOS_IPV4_NETWORK_CONTAINER = 'networkcontainer' -NIOS_IPV6_NETWORK_CONTAINER = 'ipv6networkcontainer' -NIOS_MEMBER = 'member' - -NIOS_PROVIDER_SPEC = { - 'host': dict(fallback=(env_fallback, ['INFOBLOX_HOST'])), - 'username': dict(fallback=(env_fallback, ['INFOBLOX_USERNAME'])), - 'password': dict(fallback=(env_fallback, ['INFOBLOX_PASSWORD']), no_log=True), - 'validate_certs': dict(type='bool', default=False, fallback=(env_fallback, ['INFOBLOX_SSL_VERIFY']), aliases=['ssl_verify']), - 'silent_ssl_warnings': dict(type='bool', default=True), - 'http_request_timeout': dict(type='int', default=10, fallback=(env_fallback, ['INFOBLOX_HTTP_REQUEST_TIMEOUT'])), - 'http_pool_connections': dict(type='int', default=10), - 'http_pool_maxsize': dict(type='int', default=10), - 'max_retries': dict(type='int', default=3, fallback=(env_fallback, ['INFOBLOX_MAX_RETRIES'])), - 'wapi_version': dict(default='2.1', fallback=(env_fallback, ['INFOBLOX_WAP_VERSION'])), - 'max_results': dict(type='int', default=1000, fallback=(env_fallback, ['INFOBLOX_MAX_RETRIES'])) -} - - -def get_connector(*args, **kwargs): - ''' Returns an instance of infoblox_client.connector.Connector - :params args: positional arguments are silently ignored - :params kwargs: dict that is passed to Connector init - :returns: Connector - ''' - if not HAS_INFOBLOX_CLIENT: - raise Exception('infoblox-client is required but does not appear ' - 'to be installed. It can be installed using the ' - 'command `pip install infoblox-client`') - - if not set(kwargs.keys()).issubset(list(NIOS_PROVIDER_SPEC.keys()) + ['ssl_verify']): - raise Exception('invalid or unsupported keyword argument for connector') - for key, value in iteritems(NIOS_PROVIDER_SPEC): - if key not in kwargs: - # apply default values from NIOS_PROVIDER_SPEC since we cannot just - # assume the provider values are coming from AnsibleModule - if 'default' in value: - kwargs[key] = value['default'] - - # override any values with env variables unless they were - # explicitly set - env = ('INFOBLOX_%s' % key).upper() - if env in os.environ: - kwargs[key] = os.environ.get(env) - - if 'validate_certs' in kwargs.keys(): - kwargs['ssl_verify'] = kwargs['validate_certs'] - kwargs.pop('validate_certs', None) - - return Connector(kwargs) - - -def normalize_extattrs(value): - ''' Normalize extattrs field to expected format - The module accepts extattrs as key/value pairs. This method will - transform the key/value pairs into a structure suitable for - sending across WAPI in the format of: - extattrs: { - key: { - value: - } - } - ''' - return dict([(k, {'value': v}) for k, v in iteritems(value)]) - - -def flatten_extattrs(value): - ''' Flatten the key/value struct for extattrs - WAPI returns extattrs field as a dict in form of: - extattrs: { - key: { - value: - } - } - This method will flatten the structure to: - extattrs: { - key: value - } - ''' - return dict([(k, v['value']) for k, v in iteritems(value)]) - - -def member_normalize(member_spec): - ''' Transforms the member module arguments into a valid WAPI struct - This function will transform the arguments into a structure that - is a valid WAPI structure in the format of: - { - key: , - } - It will remove any arguments that are set to None since WAPI will error on - that condition. - The remainder of the value validation is performed by WAPI - Some parameters in ib_spec are passed as a list in order to pass the validation for elements. - In this function, they are converted to dictionary. - ''' - member_elements = ['vip_setting', 'ipv6_setting', 'lan2_port_setting', 'mgmt_port_setting', - 'pre_provisioning', 'network_setting', 'v6_network_setting', - 'ha_port_setting', 'lan_port_setting', 'lan2_physical_setting', - 'lan_ha_port_setting', 'mgmt_network_setting', 'v6_mgmt_network_setting'] - for key in member_spec.keys(): - if key in member_elements and member_spec[key] is not None: - member_spec[key] = member_spec[key][0] - if isinstance(member_spec[key], dict): - member_spec[key] = member_normalize(member_spec[key]) - elif isinstance(member_spec[key], list): - for x in member_spec[key]: - if isinstance(x, dict): - x = member_normalize(x) - elif member_spec[key] is None: - del member_spec[key] - return member_spec - - -class WapiBase(object): - ''' Base class for implementing Infoblox WAPI API ''' - provider_spec = {'provider': dict(type='dict', options=NIOS_PROVIDER_SPEC)} - - def __init__(self, provider): - self.connector = get_connector(**provider) - - def __getattr__(self, name): - try: - return self.__dict__[name] - except KeyError: - if name.startswith('_'): - raise AttributeError("'%s' object has no attribute '%s'" % (self.__class__.__name__, name)) - return partial(self._invoke_method, name) - - def _invoke_method(self, name, *args, **kwargs): - try: - method = getattr(self.connector, name) - return method(*args, **kwargs) - except InfobloxException as exc: - if hasattr(self, 'handle_exception'): - self.handle_exception(name, exc) - else: - raise - - -class WapiLookup(WapiBase): - ''' Implements WapiBase for lookup plugins ''' - def handle_exception(self, method_name, exc): - if ('text' in exc.response): - raise Exception(exc.response['text']) - else: - raise Exception(exc) - - -class WapiInventory(WapiBase): - ''' Implements WapiBase for dynamic inventory script ''' - pass - - -class WapiModule(WapiBase): - ''' Implements WapiBase for executing a NIOS module ''' - def __init__(self, module): - self.module = module - provider = module.params['provider'] - try: - super(WapiModule, self).__init__(provider) - except Exception as exc: - self.module.fail_json(msg=to_text(exc)) - - def handle_exception(self, method_name, exc): - ''' Handles any exceptions raised - This method will be called if an InfobloxException is raised for - any call to the instance of Connector and also, in case of generic - exception. This method will then gracefully fail the module. - :args exc: instance of InfobloxException - ''' - if ('text' in exc.response): - self.module.fail_json( - msg=exc.response['text'], - type=exc.response['Error'].split(':')[0], - code=exc.response.get('code'), - operation=method_name - ) - else: - self.module.fail_json(msg=to_native(exc)) - - def run(self, ib_obj_type, ib_spec): - ''' Runs the module and performans configuration tasks - :args ib_obj_type: the WAPI object type to operate against - :args ib_spec: the specification for the WAPI object as a dict - :returns: a results dict - ''' - - update = new_name = None - state = self.module.params['state'] - if state not in ('present', 'absent'): - self.module.fail_json(msg='state must be one of `present`, `absent`, got `%s`' % state) - - result = {'changed': False} - - obj_filter = dict([(k, self.module.params[k]) for k, v in iteritems(ib_spec) if v.get('ib_req')]) - - # get object reference - ib_obj_ref, update, new_name = self.get_object_ref(self.module, ib_obj_type, obj_filter, ib_spec) - proposed_object = {} - for key, value in iteritems(ib_spec): - if self.module.params[key] is not None: - if 'transform' in value: - proposed_object[key] = value['transform'](self.module) - else: - proposed_object[key] = self.module.params[key] - - # If configure_by_dns is set to False, then delete the default dns set in the param else throw exception - if not proposed_object.get('configure_for_dns') and proposed_object.get('view') == 'default'\ - and ib_obj_type == NIOS_HOST_RECORD: - del proposed_object['view'] - elif not proposed_object.get('configure_for_dns') and proposed_object.get('view') != 'default'\ - and ib_obj_type == NIOS_HOST_RECORD: - self.module.fail_json(msg='DNS Bypass is not allowed if DNS view is set other than \'default\'') - - if ib_obj_ref: - if len(ib_obj_ref) > 1: - for each in ib_obj_ref: - # To check for existing A_record with same name with input A_record by IP - if each.get('ipv4addr') and each.get('ipv4addr') == proposed_object.get('ipv4addr'): - current_object = each - # To check for existing Host_record with same name with input Host_record by IP - elif each.get('ipv4addrs')[0].get('ipv4addr') and each.get('ipv4addrs')[0].get('ipv4addr')\ - == proposed_object.get('ipv4addrs')[0].get('ipv4addr'): - current_object = each - # Else set the current_object with input value - else: - current_object = obj_filter - ref = None - else: - current_object = ib_obj_ref[0] - if 'extattrs' in current_object: - current_object['extattrs'] = flatten_extattrs(current_object['extattrs']) - if current_object.get('_ref'): - ref = current_object.pop('_ref') - else: - current_object = obj_filter - ref = None - # checks if the object type is member to normalize the attributes being passed - if (ib_obj_type == NIOS_MEMBER): - proposed_object = member_normalize(proposed_object) - - # checks if the name's field has been updated - if update and new_name: - proposed_object['name'] = new_name - - check_remove = [] - if (ib_obj_type == NIOS_HOST_RECORD): - # this check is for idempotency, as if the same ip address shall be passed - # add param will be removed, and same exists true for remove case as well. - if 'ipv4addrs' in [current_object and proposed_object]: - for each in current_object['ipv4addrs']: - if each['ipv4addr'] == proposed_object['ipv4addrs'][0]['ipv4addr']: - if 'add' in proposed_object['ipv4addrs'][0]: - del proposed_object['ipv4addrs'][0]['add'] - break - check_remove += each.values() - if proposed_object['ipv4addrs'][0]['ipv4addr'] not in check_remove: - if 'remove' in proposed_object['ipv4addrs'][0]: - del proposed_object['ipv4addrs'][0]['remove'] - - res = None - modified = not self.compare_objects(current_object, proposed_object) - if 'extattrs' in proposed_object: - proposed_object['extattrs'] = normalize_extattrs(proposed_object['extattrs']) - - # Checks if nios_next_ip param is passed in ipv4addrs/ipv4addr args - proposed_object = self.check_if_nios_next_ip_exists(proposed_object) - - if state == 'present': - if ref is None: - if not self.module.check_mode: - self.create_object(ib_obj_type, proposed_object) - result['changed'] = True - # Check if NIOS_MEMBER and the flag to call function create_token is set - elif (ib_obj_type == NIOS_MEMBER) and (proposed_object['create_token']): - proposed_object = None - # the function creates a token that can be used by a pre-provisioned member to join the grid - result['api_results'] = self.call_func('create_token', ref, proposed_object) - result['changed'] = True - elif modified: - if 'ipv4addrs' in proposed_object: - if ('add' not in proposed_object['ipv4addrs'][0]) and ('remove' not in proposed_object['ipv4addrs'][0]): - self.check_if_recordname_exists(obj_filter, ib_obj_ref, ib_obj_type, current_object, proposed_object) - - if (ib_obj_type in (NIOS_HOST_RECORD, NIOS_NETWORK_VIEW, NIOS_DNS_VIEW)): - run_update = True - proposed_object = self.on_update(proposed_object, ib_spec) - if 'ipv4addrs' in proposed_object: - if ('add' or 'remove') in proposed_object['ipv4addrs'][0]: - run_update, proposed_object = self.check_if_add_remove_ip_arg_exists(proposed_object) - if run_update: - res = self.update_object(ref, proposed_object) - result['changed'] = True - else: - res = ref - if (ib_obj_type in (NIOS_A_RECORD, NIOS_AAAA_RECORD, NIOS_PTR_RECORD, NIOS_SRV_RECORD)): - # popping 'view' key as update of 'view' is not supported with respect to a:record/aaaa:record/srv:record/ptr:record - proposed_object = self.on_update(proposed_object, ib_spec) - del proposed_object['view'] - if not self.module.check_mode: - res = self.update_object(ref, proposed_object) - result['changed'] = True - elif 'network_view' in proposed_object: - proposed_object.pop('network_view') - result['changed'] = True - if not self.module.check_mode and res is None: - proposed_object = self.on_update(proposed_object, ib_spec) - self.update_object(ref, proposed_object) - result['changed'] = True - - elif state == 'absent': - if ref is not None: - if 'ipv4addrs' in proposed_object: - if 'remove' in proposed_object['ipv4addrs'][0]: - self.check_if_add_remove_ip_arg_exists(proposed_object) - self.update_object(ref, proposed_object) - result['changed'] = True - elif not self.module.check_mode: - self.delete_object(ref) - result['changed'] = True - - return result - - def check_if_recordname_exists(self, obj_filter, ib_obj_ref, ib_obj_type, current_object, proposed_object): - ''' Send POST request if host record input name and retrieved ref name is same, - but input IP and retrieved IP is different''' - - if 'name' in (obj_filter and ib_obj_ref[0]) and ib_obj_type == NIOS_HOST_RECORD: - obj_host_name = obj_filter['name'] - ref_host_name = ib_obj_ref[0]['name'] - if 'ipv4addrs' in (current_object and proposed_object): - current_ip_addr = current_object['ipv4addrs'][0]['ipv4addr'] - proposed_ip_addr = proposed_object['ipv4addrs'][0]['ipv4addr'] - elif 'ipv6addrs' in (current_object and proposed_object): - current_ip_addr = current_object['ipv6addrs'][0]['ipv6addr'] - proposed_ip_addr = proposed_object['ipv6addrs'][0]['ipv6addr'] - - if obj_host_name == ref_host_name and current_ip_addr != proposed_ip_addr: - self.create_object(ib_obj_type, proposed_object) - - def check_if_nios_next_ip_exists(self, proposed_object): - ''' Check if nios_next_ip argument is passed in ipaddr while creating - host record, if yes then format proposed object ipv4addrs and pass - func:nextavailableip and ipaddr range to create hostrecord with next - available ip in one call to avoid any race condition ''' - - if 'ipv4addrs' in proposed_object: - if 'nios_next_ip' in proposed_object['ipv4addrs'][0]['ipv4addr']: - ip_range = self.module._check_type_dict(proposed_object['ipv4addrs'][0]['ipv4addr'])['nios_next_ip'] - proposed_object['ipv4addrs'][0]['ipv4addr'] = NIOS_NEXT_AVAILABLE_IP + ':' + ip_range - elif 'ipv4addr' in proposed_object: - if 'nios_next_ip' in proposed_object['ipv4addr']: - ip_range = self.module._check_type_dict(proposed_object['ipv4addr'])['nios_next_ip'] - proposed_object['ipv4addr'] = NIOS_NEXT_AVAILABLE_IP + ':' + ip_range - - return proposed_object - - def check_if_add_remove_ip_arg_exists(self, proposed_object): - ''' - This function shall check if add/remove param is set to true and - is passed in the args, then we will update the proposed dictionary - to add/remove IP to existing host_record, if the user passes false - param with the argument nothing shall be done. - :returns: True if param is changed based on add/remove, and also the - changed proposed_object. - ''' - update = False - if 'add' in proposed_object['ipv4addrs'][0]: - if proposed_object['ipv4addrs'][0]['add']: - proposed_object['ipv4addrs+'] = proposed_object['ipv4addrs'] - del proposed_object['ipv4addrs'] - del proposed_object['ipv4addrs+'][0]['add'] - update = True - else: - del proposed_object['ipv4addrs'][0]['add'] - elif 'remove' in proposed_object['ipv4addrs'][0]: - if proposed_object['ipv4addrs'][0]['remove']: - proposed_object['ipv4addrs-'] = proposed_object['ipv4addrs'] - del proposed_object['ipv4addrs'] - del proposed_object['ipv4addrs-'][0]['remove'] - update = True - else: - del proposed_object['ipv4addrs'][0]['remove'] - return update, proposed_object - - def issubset(self, item, objects): - ''' Checks if item is a subset of objects - :args item: the subset item to validate - :args objects: superset list of objects to validate against - :returns: True if item is a subset of one entry in objects otherwise - this method will return None - ''' - for obj in objects: - if isinstance(item, dict): - if all(entry in obj.items() for entry in item.items()): - return True - else: - if item in obj: - return True - - def compare_objects(self, current_object, proposed_object): - for key, proposed_item in iteritems(proposed_object): - current_item = current_object.get(key) - - # if proposed has a key that current doesn't then the objects are - # not equal and False will be immediately returned - if current_item is None: - return False - - elif isinstance(proposed_item, list): - for subitem in proposed_item: - if not self.issubset(subitem, current_item): - return False - - elif isinstance(proposed_item, dict): - return self.compare_objects(current_item, proposed_item) - - else: - if current_item != proposed_item: - return False - - return True - - def get_object_ref(self, module, ib_obj_type, obj_filter, ib_spec): - ''' this function gets the reference object of pre-existing nios objects ''' - - update = False - old_name = new_name = None - if ('name' in obj_filter): - # gets and returns the current object based on name/old_name passed - try: - name_obj = self.module._check_type_dict(obj_filter['name']) - old_name = name_obj['old_name'] - new_name = name_obj['new_name'] - except TypeError: - name = obj_filter['name'] - - if old_name and new_name: - if (ib_obj_type == NIOS_HOST_RECORD): - test_obj_filter = dict([('name', old_name), ('view', obj_filter['view'])]) - elif (ib_obj_type in (NIOS_AAAA_RECORD, NIOS_A_RECORD)): - test_obj_filter = obj_filter - else: - test_obj_filter = dict([('name', old_name)]) - # get the object reference - ib_obj = self.get_object(ib_obj_type, test_obj_filter, return_fields=ib_spec.keys()) - if ib_obj: - obj_filter['name'] = new_name - else: - test_obj_filter['name'] = new_name - ib_obj = self.get_object(ib_obj_type, test_obj_filter, return_fields=ib_spec.keys()) - update = True - return ib_obj, update, new_name - if (ib_obj_type == NIOS_HOST_RECORD): - # to check only by name if dns bypassing is set - if not obj_filter['configure_for_dns']: - test_obj_filter = dict([('name', name)]) - else: - test_obj_filter = dict([('name', name), ('view', obj_filter['view'])]) - elif (ib_obj_type == NIOS_IPV4_FIXED_ADDRESS or ib_obj_type == NIOS_IPV6_FIXED_ADDRESS and 'mac' in obj_filter): - test_obj_filter = dict([['mac', obj_filter['mac']]]) - elif (ib_obj_type == NIOS_A_RECORD): - # resolves issue where a_record with uppercase name was returning null and was failing - test_obj_filter = obj_filter - test_obj_filter['name'] = test_obj_filter['name'].lower() - # resolves issue where multiple a_records with same name and different IP address - try: - ipaddr_obj = self.module._check_type_dict(obj_filter['ipv4addr']) - ipaddr = ipaddr_obj['old_ipv4addr'] - except TypeError: - ipaddr = obj_filter['ipv4addr'] - test_obj_filter['ipv4addr'] = ipaddr - elif (ib_obj_type == NIOS_TXT_RECORD): - # resolves issue where multiple txt_records with same name and different text - test_obj_filter = obj_filter - try: - text_obj = self.module._check_type_dict(obj_filter['text']) - txt = text_obj['old_text'] - except TypeError: - txt = obj_filter['text'] - test_obj_filter['text'] = txt - # check if test_obj_filter is empty copy passed obj_filter - else: - test_obj_filter = obj_filter - ib_obj = self.get_object(ib_obj_type, test_obj_filter.copy(), return_fields=ib_spec.keys()) - elif (ib_obj_type == NIOS_A_RECORD): - # resolves issue where multiple a_records with same name and different IP address - test_obj_filter = obj_filter - try: - ipaddr_obj = self.module._check_type_dict(obj_filter['ipv4addr']) - ipaddr = ipaddr_obj['old_ipv4addr'] - except TypeError: - ipaddr = obj_filter['ipv4addr'] - test_obj_filter['ipv4addr'] = ipaddr - ib_obj = self.get_object(ib_obj_type, test_obj_filter.copy(), return_fields=ib_spec.keys()) - elif (ib_obj_type == NIOS_TXT_RECORD): - # resolves issue where multiple txt_records with same name and different text - test_obj_filter = obj_filter - try: - text_obj = self.module._check_type_dict(obj_filter['text']) - txt = text_obj['old_text'] - except TypeError: - txt = obj_filter['text'] - test_obj_filter['text'] = txt - ib_obj = self.get_object(ib_obj_type, test_obj_filter.copy(), return_fields=ib_spec.keys()) - elif (ib_obj_type == NIOS_ZONE): - # del key 'restart_if_needed' as nios_zone get_object fails with the key present - temp = ib_spec['restart_if_needed'] - del ib_spec['restart_if_needed'] - ib_obj = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=ib_spec.keys()) - # reinstate restart_if_needed if ib_obj is none, meaning there's no existing nios_zone ref - if not ib_obj: - ib_spec['restart_if_needed'] = temp - elif (ib_obj_type == NIOS_MEMBER): - # del key 'create_token' as nios_member get_object fails with the key present - temp = ib_spec['create_token'] - del ib_spec['create_token'] - ib_obj = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=ib_spec.keys()) - if temp: - # reinstate 'create_token' key - ib_spec['create_token'] = temp - else: - ib_obj = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=ib_spec.keys()) - return ib_obj, update, new_name - - def on_update(self, proposed_object, ib_spec): - ''' Event called before the update is sent to the API endpoing - This method will allow the final proposed object to be changed - and/or keys filtered before it is sent to the API endpoint to - be processed. - :args proposed_object: A dict item that will be encoded and sent - the API endpoint with the updated data structure - :returns: updated object to be sent to API endpoint - ''' - keys = set() - for key, value in iteritems(proposed_object): - update = ib_spec[key].get('update', True) - if not update: - keys.add(key) - return dict([(k, v) for k, v in iteritems(proposed_object) if k not in keys]) diff --git a/test/support/integration/plugins/modules/nios_txt_record.py b/test/support/integration/plugins/modules/nios_txt_record.py deleted file mode 100644 index b9e63dfc6eb..00000000000 --- a/test/support/integration/plugins/modules/nios_txt_record.py +++ /dev/null @@ -1,134 +0,0 @@ -#!/usr/bin/python -# Copyright (c) 2018 Red Hat, Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified'} - - -DOCUMENTATION = ''' ---- -module: nios_txt_record -version_added: "2.7" -author: "Corey Wanless (@coreywan)" -short_description: Configure Infoblox NIOS txt records -description: - - Adds and/or removes instances of txt record objects from - Infoblox NIOS servers. This module manages NIOS C(record:txt) objects - using the Infoblox WAPI interface over REST. -requirements: - - infoblox_client -extends_documentation_fragment: nios -options: - name: - description: - - Specifies the fully qualified hostname to add or remove from - the system - required: true - view: - description: - - Sets the DNS view to associate this tst record with. The DNS - view must already be configured on the system - required: true - default: default - aliases: - - dns_view - text: - description: - - Text associated with the record. It can contain up to 255 bytes - per substring, up to a total of 512 bytes. To enter leading, - trailing, or embedded spaces in the text, add quotes around the - text to preserve the spaces. - required: true - ttl: - description: - - Configures the TTL to be associated with this tst record - extattrs: - description: - - Allows for the configuration of Extensible Attributes on the - instance of the object. This argument accepts a set of key / value - pairs for configuration. - comment: - description: - - Configures a text string comment to be associated with the instance - of this object. The provided text string will be configured on the - object instance. - state: - description: - - Configures the intended state of the instance of the object on - the NIOS server. When this value is set to C(present), the object - is configured on the device and when this value is set to C(absent) - the value is removed (if necessary) from the device. - default: present - choices: - - present - - absent -''' - -EXAMPLES = ''' - - name: Ensure a text Record Exists - nios_txt_record: - name: fqdn.txt.record.com - text: mytext - state: present - view: External - provider: - host: "{{ inventory_hostname_short }}" - username: admin - password: admin - - - name: Ensure a text Record does not exist - nios_txt_record: - name: fqdn.txt.record.com - text: mytext - state: absent - view: External - provider: - host: "{{ inventory_hostname_short }}" - username: admin - password: admin -''' - -RETURN = ''' # ''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.six import iteritems -from ansible.module_utils.net_tools.nios.api import WapiModule - - -def main(): - ''' Main entry point for module execution - ''' - - ib_spec = dict( - name=dict(required=True, ib_req=True), - view=dict(default='default', aliases=['dns_view'], ib_req=True), - text=dict(ib_req=True), - ttl=dict(type='int'), - extattrs=dict(type='dict'), - comment=dict(), - ) - - argument_spec = dict( - provider=dict(required=True), - state=dict(default='present', choices=['present', 'absent']) - ) - - argument_spec.update(ib_spec) - argument_spec.update(WapiModule.provider_spec) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) - - wapi = WapiModule(module) - result = wapi.run('record:txt', ib_spec) - - module.exit_json(**result) - - -if __name__ == '__main__': - main() diff --git a/test/support/integration/plugins/modules/nios_zone.py b/test/support/integration/plugins/modules/nios_zone.py deleted file mode 100644 index 0ffb2ff0a40..00000000000 --- a/test/support/integration/plugins/modules/nios_zone.py +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/python -# Copyright (c) 2018 Red Hat, Inc. -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function -__metaclass__ = type - -ANSIBLE_METADATA = {'metadata_version': '1.1', - 'status': ['preview'], - 'supported_by': 'certified'} - - -DOCUMENTATION = ''' ---- -module: nios_zone -version_added: "2.5" -author: "Peter Sprygada (@privateip)" -short_description: Configure Infoblox NIOS DNS zones -description: - - Adds and/or removes instances of DNS zone objects from - Infoblox NIOS servers. This module manages NIOS C(zone_auth) objects - using the Infoblox WAPI interface over REST. -requirements: - - infoblox-client -extends_documentation_fragment: nios -options: - fqdn: - description: - - Specifies the qualified domain name to either add or remove from - the NIOS instance based on the configured C(state) value. - required: true - aliases: - - name - view: - description: - - Configures the DNS view name for the configured resource. The - specified DNS zone must already exist on the running NIOS instance - prior to configuring zones. - required: true - default: default - aliases: - - dns_view - grid_primary: - description: - - Configures the grid primary servers for this zone. - suboptions: - name: - description: - - The name of the grid primary server - grid_secondaries: - description: - - Configures the grid secondary servers for this zone. - suboptions: - name: - description: - - The name of the grid secondary server - ns_group: - version_added: "2.6" - description: - - Configures the name server group for this zone. Name server group is - mutually exclusive with grid primary and grid secondaries. - restart_if_needed: - version_added: "2.6" - description: - - If set to true, causes the NIOS DNS service to restart and load the - new zone configuration - type: bool - zone_format: - version_added: "2.7" - description: - - Create an authorative Reverse-Mapping Zone which is an area of network - space for which one or more name servers-primary and secondary-have the - responsibility to respond to address-to-name queries. It supports - reverse-mapping zones for both IPv4 and IPv6 addresses. - default: FORWARD - extattrs: - description: - - Allows for the configuration of Extensible Attributes on the - instance of the object. This argument accepts a set of key / value - pairs for configuration. - comment: - description: - - Configures a text string comment to be associated with the instance - of this object. The provided text string will be configured on the - object instance. - state: - description: - - Configures the intended state of the instance of the object on - the NIOS server. When this value is set to C(present), the object - is configured on the device and when this value is set to C(absent) - the value is removed (if necessary) from the device. - default: present - choices: - - present - - absent -''' - -EXAMPLES = ''' -- name: configure a zone on the system using grid primary and secondaries - nios_zone: - name: ansible.com - grid_primary: - - name: gridprimary.grid.com - grid_secondaries: - - name: gridsecondary1.grid.com - - name: gridsecondary2.grid.com - restart_if_needed: true - state: present - provider: - host: "{{ inventory_hostname_short }}" - username: admin - password: admin - connection: local -- name: configure a zone on the system using a name server group - nios_zone: - name: ansible.com - ns_group: examplensg - restart_if_needed: true - state: present - provider: - host: "{{ inventory_hostname_short }}" - username: admin - password: admin - connection: local -- name: configure a reverse mapping zone on the system using IPV4 zone format - nios_zone: - name: 10.10.10.0/24 - zone_format: IPV4 - state: present - provider: - host: "{{ inventory_hostname_short }}" - username: admin - password: admin - connection: local -- name: configure a reverse mapping zone on the system using IPV6 zone format - nios_zone: - name: 100::1/128 - zone_format: IPV6 - state: present - provider: - host: "{{ inventory_hostname_short }}" - username: admin - password: admin - connection: local -- name: update the comment and ext attributes for an existing zone - nios_zone: - name: ansible.com - comment: this is an example comment - extattrs: - Site: west-dc - state: present - provider: - host: "{{ inventory_hostname_short }}" - username: admin - password: admin - connection: local -- name: remove the dns zone - nios_zone: - name: ansible.com - state: absent - provider: - host: "{{ inventory_hostname_short }}" - username: admin - password: admin - connection: local -- name: remove the reverse mapping dns zone from the system with IPV4 zone format - nios_zone: - name: 10.10.10.0/24 - zone_format: IPV4 - state: absent - provider: - host: "{{ inventory_hostname_short }}" - username: admin - password: admin - connection: local -''' - -RETURN = ''' # ''' - -from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.net_tools.nios.api import WapiModule -from ansible.module_utils.net_tools.nios.api import NIOS_ZONE - - -def main(): - ''' Main entry point for module execution - ''' - grid_spec = dict( - name=dict(required=True), - ) - - ib_spec = dict( - fqdn=dict(required=True, aliases=['name'], ib_req=True, update=False), - zone_format=dict(default='FORWARD', aliases=['zone_format'], ib_req=False), - view=dict(default='default', aliases=['dns_view'], ib_req=True), - - grid_primary=dict(type='list', elements='dict', options=grid_spec), - grid_secondaries=dict(type='list', elements='dict', options=grid_spec), - ns_group=dict(), - restart_if_needed=dict(type='bool'), - - extattrs=dict(type='dict'), - comment=dict() - ) - - argument_spec = dict( - provider=dict(required=True), - state=dict(default='present', choices=['present', 'absent']) - ) - - argument_spec.update(ib_spec) - argument_spec.update(WapiModule.provider_spec) - - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True, - mutually_exclusive=[ - ['ns_group', 'grid_primary'], - ['ns_group', 'grid_secondaries'] - ]) - - wapi = WapiModule(module) - result = wapi.run(NIOS_ZONE, ib_spec) - - module.exit_json(**result) - - -if __name__ == '__main__': - main()