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
This commit is contained in:
parent
4197666179
commit
6f4aed5377
|
@ -12,12 +12,14 @@ def main():
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
{
|
{
|
||||||
'state': {
|
'state': {
|
||||||
'default': 'present',
|
|
||||||
'type': 'str',
|
'type': 'str',
|
||||||
'choices': ['absent', 'present'],
|
'choices': ['absent', 'present'],
|
||||||
},
|
},
|
||||||
'path': {},
|
'path': {},
|
||||||
'content': {},
|
'content': {},
|
||||||
|
'mapping': {
|
||||||
|
'type': 'dict',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required_if=(
|
required_if=(
|
||||||
('state', 'present', ('path', 'content'), True),
|
('state', 'present', ('path', 'content'), True),
|
||||||
|
|
|
@ -24,6 +24,36 @@
|
||||||
register: argspec_mutually_exclusive_fail
|
register: argspec_mutually_exclusive_fail
|
||||||
ignore_errors: true
|
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:
|
- assert:
|
||||||
that:
|
that:
|
||||||
- argspec_simple_good is successful
|
- argspec_simple_good is successful
|
||||||
|
@ -31,3 +61,15 @@
|
||||||
- argspec_required_if_good1 is successful
|
- argspec_required_if_good1 is successful
|
||||||
- argspec_required_if_good2 is successful
|
- argspec_required_if_good2 is successful
|
||||||
- argspec_mutually_exclusive_fail is failed
|
- 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
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
hidden
|
|
|
@ -1,3 +0,0 @@
|
||||||
shippable/cloud/incidental
|
|
||||||
cloud/nios
|
|
||||||
destructive
|
|
|
@ -1,3 +0,0 @@
|
||||||
---
|
|
||||||
testcase: "*"
|
|
||||||
test_items: []
|
|
|
@ -1,2 +0,0 @@
|
||||||
dependencies:
|
|
||||||
- incidental_nios_prepare_tests
|
|
|
@ -1 +0,0 @@
|
||||||
- include: nios_txt_record_idempotence.yml
|
|
|
@ -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"
|
|
|
@ -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/k8s/raw.py metaclass-boilerplate
|
||||||
test/support/integration/plugins/module_utils/mysql.py future-import-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/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 future-import-boilerplate
|
||||||
test/support/integration/plugins/module_utils/network/common/utils.py metaclass-boilerplate
|
test/support/integration/plugins/module_utils/network/common/utils.py metaclass-boilerplate
|
||||||
test/support/integration/plugins/module_utils/postgres.py future-import-boilerplate
|
test/support/integration/plugins/module_utils/postgres.py future-import-boilerplate
|
||||||
|
|
|
@ -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: <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: <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: <value>,
|
|
||||||
}
|
|
||||||
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])
|
|
|
@ -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()
|
|
|
@ -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()
|
|
Loading…
Reference in a new issue