Skip to content
This repository has been archived by the owner on Jan 18, 2022. It is now read-only.

Commit

Permalink
Fix for updating the name in case of host record and network view and…
Browse files Browse the repository at this point in the history
… also display meaningful error in case of connection timeout (ansible#40597)

* To fix following github issues 35774, 36574 and 39494

* To fix following github issues 35774, 36574 and 39494

* To fix following github issues 35774, 36574 and 39494

* To fix following github issues 35774, 36574 and 39494

* To fix following github issues 35774, 36574 and 39494

* To fix following github issues 35774, 36574 and 39494

* removed old_name new entry to make ui cleaner

* removed old_name new entry to make ui cleaner

* removed old_name new entry to make ui cleaner

* removed old_name new entry to make ui cleaner

* removed old_name new entry to make ui cleaner

* removed old_name new entry to make ui cleaner

* to resolve the bug 40709

* reslove shippable error

* reslove shippable error

* reslove shippable error

* reslove shippable error

* reslove shippable error

* reslove shippable error

* reslove shippable error

* reslove shippable error

* reslove shippable error

* to fix shippable nios automation error

* modified the name input parsing method

* modified the name input parsing method

* modified the name input parsing method

* modified the name input parsing method

* modified the name input parsing method

* modified the name input parsing method

* modified the name input parsing method

* modified the name input parsing method

* modified the name input parsing method

* shippable error fix

* shippable error fix

* shippable error fix

* shippable error fix

* shippable error fix

* review comment fix

* shippable error fix

* shippable error fix
  • Loading branch information
justjais authored May 31, 2018
1 parent 6d3a178 commit 8151097
Show file tree
Hide file tree
Showing 13 changed files with 156 additions and 135 deletions.
154 changes: 76 additions & 78 deletions lib/ansible/module_utils/net_tools/nios/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,9 @@
# 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

Expand All @@ -40,7 +39,15 @@
except ImportError:
HAS_INFOBLOX_CLIENT = False

nios_provider_spec = {
# 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_PROVIDER_SPEC = {
'host': dict(),
'username': dict(),
'password': dict(no_log=True),
Expand All @@ -57,23 +64,20 @@

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(nios_provider_spec.keys()):
if not set(kwargs.keys()).issubset(NIOS_PROVIDER_SPEC.keys()):
raise Exception('invalid or unsupported keyword argument for connector')

for key, value in iteritems(nios_provider_spec):
for key, value in iteritems(NIOS_PROVIDER_SPEC):
if key not in kwargs:
# apply default values from nios_provider_spec since we cannot just
# 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']
Expand All @@ -89,11 +93,9 @@ def get_connector(*args, **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>
Expand All @@ -105,29 +107,23 @@ def normalize_extattrs(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)])


class WapiBase(object):
''' Base class for implementing Infoblox WAPI API '''

provider_spec = {'provider': dict(type='dict', options=nios_provider_spec)}
provider_spec = {'provider': dict(type='dict', options=NIOS_PROVIDER_SPEC)}

def __init__(self, provider):
self.connector = get_connector(**provider)
Expand Down Expand Up @@ -163,7 +159,6 @@ class WapiInventory(WapiBase):

class WapiModule(WapiBase):
''' Implements WapiBase for executing a NIOS module '''

def __init__(self, module):
self.module = module
provider = module.params['provider']
Expand All @@ -174,40 +169,44 @@ def __init__(self, module):

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. This method will then
gracefully fail the module.
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
'''
self.module.fail_json(
msg=exc.response['text'],
type=exc.response['Error'].split(':')[0],
code=exc.response.get('code'),
operation=method_name
)
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')])
ib_obj = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=ib_spec.keys())

if ib_obj:
current_object = ib_obj[0]
if('name' in obj_filter):
ib_obj_ref, update, new_name = self.get_object_ref(ib_obj_type, obj_filter, ib_spec)
else:
ib_obj_ref = self.get_object(ib_obj_type, obj_filter.copy(), return_fields=ib_spec.keys())

if ib_obj_ref:
current_object = ib_obj_ref[0]
if 'extattrs' in current_object:
current_object['extattrs'] = flatten_extattrs(current_object['extattrs'])
ref = current_object.pop('_ref')
Expand All @@ -223,25 +222,28 @@ def run(self, ib_obj_type, ib_spec):
else:
proposed_object[key] = self.module.params[key]

modified = not self.compare_objects(current_object, proposed_object)
# checks if the name's field has been updated
if update and new_name:
proposed_object['name'] = new_name

res = None
modified = not self.compare_objects(current_object, proposed_object)
if 'extattrs' in proposed_object:
proposed_object['extattrs'] = normalize_extattrs(proposed_object['extattrs'])

if state == 'present':
if ref is None:
if not self.module.check_mode:
self.create_object(ib_obj_type, proposed_object)
result['changed'] = True
elif modified:
if 'network_view' in proposed_object:
self.check_if_network_view_exists(proposed_object['network_view'])
proposed_object.pop('network_view')
elif 'view' in proposed_object:
self.check_if_dns_view_exists(proposed_object['view'])
if not self.module.check_mode:
if (ib_obj_type in (NIOS_HOST_RECORD, NIOS_NETWORK_VIEW, NIOS_DNS_VIEW)):
proposed_object = self.on_update(proposed_object, ib_spec)
res = self.update_object(ref, proposed_object)
elif 'network_view' in proposed_object:
proposed_object.pop('network_view')
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':
Expand All @@ -252,42 +254,10 @@ def run(self, ib_obj_type, ib_spec):

return result

def check_if_dns_view_exists(self, name, fail_on_missing=True):
''' Checks if the specified DNS view is already configured
:args name: the name of the DNS view to check
:args fail_on_missing: fail the module if the DNS view does not exist
:returns: True if the network_view exists and False if the DNS view
does not exist and fail_on_missing is False
'''
res = self.get_object('view', {'name': name}) is not None
if not res and fail_on_missing:
self.module.fail_json(msg='DNS view %s does not exist, please create '
'it using nios_dns_view first' % name)
return res

def check_if_network_view_exists(self, name, fail_on_missing=True):
''' Checks if the specified network_view is already configured
:args name: the name of the network view to check
:args fail_on_missing: fail the module if the network_view does not exist
:returns: True if the network_view exists and False if the network_view
does not exist and fail_on_missing is False
'''
res = self.get_object('networkview', {'name': name}) is not None
if not res and fail_on_missing:
self.module.fail_json(msg='Network view %s does not exist, please create '
'it using nios_network_view first' % name)
return res

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
'''
Expand Down Expand Up @@ -322,22 +292,50 @@ def compare_objects(self, current_object, proposed_object):

return True

def get_object_ref(self, ib_obj_type, obj_filter, ib_spec):
''' this function gets and returns the current object based on name/old_name passed'''
update = False
old_name = new_name = None
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'])])
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):
test_obj_filter = dict([('name', name), ('view', obj_filter['view'])])
else:
test_obj_filter = dict([('name', name)])
ib_obj = self.get_object(ib_obj_type, test_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 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])
20 changes: 15 additions & 5 deletions lib/ansible/modules/net_tools/nios/nios_dns_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@
- Adds and/or removes instances of DNS view objects from
Infoblox NIOS servers. This module manages NIOS C(view) objects
using the Infoblox WAPI interface over REST.
- Updates instances of DNS view object from Infoblox NIOS servers.
requirements:
- infoblox_client
extends_documentation_fragment: nios
options:
name:
description:
- Specifies the name of the DNS view to add and/or remove from the
system configuration based on the setting of the C(state) argument.
- Specifies the fully qualified hostname to add or remove from
the system. User can also update the hostname as it is possible
to pass a dict containing I(new_name), I(old_name). See examples.
required: true
aliases:
- view
Expand Down Expand Up @@ -73,7 +75,6 @@
username: admin
password: admin
connection: local
- name: update the comment for dns view
nios_dns_view:
name: ansible-dns
Expand All @@ -84,7 +85,6 @@
username: admin
password: admin
connection: local
- name: remove the dns view instance
nios_dns_view:
name: ansible-dns
Expand All @@ -94,12 +94,22 @@
username: admin
password: admin
connection: local
- name: update the dns view instance
nios_dns_view:
name: {new_name: ansible-dns-new, old_name: ansible-dns}
state: present
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_DNS_VIEW


def main():
Expand All @@ -125,7 +135,7 @@ def main():
supports_check_mode=True)

wapi = WapiModule(module)
result = wapi.run('view', ib_spec)
result = wapi.run(NIOS_DNS_VIEW, ib_spec)

module.exit_json(**result)

Expand Down
Loading

0 comments on commit 8151097

Please sign in to comment.