mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-06-05 07:49:10 -07:00
Fix for updating the name in case of host record and network view and also display meaningful error in case of connection timeout (#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
This commit is contained in:
parent
6d3a1786cc
commit
81510970ae
13 changed files with 156 additions and 135 deletions
|
@ -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
|
||||
|
||||
|
@ -40,7 +39,15 @@ try:
|
|||
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),
|
||||
|
@ -57,10 +64,8 @@ nios_provider_spec = {
|
|||
|
||||
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:
|
||||
|
@ -68,12 +73,11 @@ def get_connector(*args, **kwargs):
|
|||
'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']
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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']
|
||||
|
@ -174,29 +169,29 @@ class WapiModule(WapiBase):
|
|||
|
||||
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)
|
||||
|
@ -204,10 +199,14 @@ class WapiModule(WapiBase):
|
|||
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')
|
||||
|
@ -223,25 +222,28 @@ class WapiModule(WapiBase):
|
|||
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':
|
||||
|
@ -252,42 +254,10 @@ class WapiModule(WapiBase):
|
|||
|
||||
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
|
||||
'''
|
||||
|
@ -322,16 +292,45 @@ class WapiModule(WapiBase):
|
|||
|
||||
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()
|
||||
|
@ -339,5 +338,4 @@ class WapiModule(WapiBase):
|
|||
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])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue