mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
refactors nios api shared code to handle provider better (#35393)
* refactors nios api shared code to handle provider better This change refactors the shared code to be easily shared between modules, plugins and dynamic inventory scripts. All parts now implement the provider arguments uniformly. This also provides a centralized fix to suppress urllib3 warnings coming from the requests library implemented by infoblox_client * fix up pep8 errors * fix missing var name
This commit is contained in:
parent
1f1402ea68
commit
c2d3b9cbd5
10 changed files with 90 additions and 70 deletions
|
@ -25,18 +25,9 @@ import argparse
|
||||||
from ansible.parsing.dataloader import DataLoader
|
from ansible.parsing.dataloader import DataLoader
|
||||||
from ansible.module_utils.six import iteritems
|
from ansible.module_utils.six import iteritems
|
||||||
from ansible.module_utils._text import to_text
|
from ansible.module_utils._text import to_text
|
||||||
from ansible.module_utils.net_tools.nios.api import get_connector
|
from ansible.module_utils.net_tools.nios.api import WapiInventory
|
||||||
from ansible.module_utils.net_tools.nios.api import normalize_extattrs, flatten_extattrs
|
from ansible.module_utils.net_tools.nios.api import normalize_extattrs, flatten_extattrs
|
||||||
|
|
||||||
try:
|
|
||||||
# disable urllib3 warnings so as to not interfere with printing to stdout
|
|
||||||
# which is read by ansible
|
|
||||||
import urllib3
|
|
||||||
urllib3.disable_warnings()
|
|
||||||
except ImportError:
|
|
||||||
sys.stdout.write('missing required library: urllib3\n')
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
|
|
||||||
CONFIG_FILES = [
|
CONFIG_FILES = [
|
||||||
'/etc/ansible/infoblox.yaml',
|
'/etc/ansible/infoblox.yaml',
|
||||||
|
@ -70,7 +61,7 @@ def main():
|
||||||
loader = DataLoader()
|
loader = DataLoader()
|
||||||
config = loader.load_from_file(config_file)
|
config = loader.load_from_file(config_file)
|
||||||
provider = config.get('provider') or {}
|
provider = config.get('provider') or {}
|
||||||
connector = get_connector(**provider)
|
wapi = WapiInventory(provider)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
sys.stdout.write(to_text(exc))
|
sys.stdout.write(to_text(exc))
|
||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
@ -99,7 +90,7 @@ def main():
|
||||||
|
|
||||||
return_fields = ['name', 'view', 'extattrs', 'ipv4addrs']
|
return_fields = ['name', 'view', 'extattrs', 'ipv4addrs']
|
||||||
|
|
||||||
hosts = connector.get_object('record:host',
|
hosts = wapi.get_object('record:host',
|
||||||
host_filter,
|
host_filter,
|
||||||
extattrs=extattrs,
|
extattrs=extattrs,
|
||||||
return_fields=return_fields)
|
return_fields=return_fields)
|
||||||
|
|
|
@ -45,6 +45,7 @@ nios_provider_spec = {
|
||||||
'username': dict(),
|
'username': dict(),
|
||||||
'password': dict(no_log=True),
|
'password': dict(no_log=True),
|
||||||
'ssl_verify': dict(type='bool', default=False),
|
'ssl_verify': dict(type='bool', default=False),
|
||||||
|
'silent_ssl_warnings': dict(type='bool', default=True),
|
||||||
'http_request_timeout': dict(type='int', default=10),
|
'http_request_timeout': dict(type='int', default=10),
|
||||||
'http_pool_connections': dict(type='int', default=10),
|
'http_pool_connections': dict(type='int', default=10),
|
||||||
'http_pool_maxsize': dict(type='int', default=10),
|
'http_pool_maxsize': dict(type='int', default=10),
|
||||||
|
@ -53,11 +54,14 @@ nios_provider_spec = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_provider_spec():
|
|
||||||
return {'provider': dict(type='dict', options=nios_provider_spec)}
|
|
||||||
|
|
||||||
|
|
||||||
def get_connector(*args, **kwargs):
|
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:
|
if not HAS_INFOBLOX_CLIENT:
|
||||||
raise Exception('infoblox-client is required but does not appear '
|
raise Exception('infoblox-client is required but does not appear '
|
||||||
'to be installed. It can be installed using the '
|
'to be installed. It can be installed using the '
|
||||||
|
@ -66,8 +70,15 @@ def get_connector(*args, **kwargs):
|
||||||
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')
|
raise Exception('invalid or unsupported keyword argument for connector')
|
||||||
|
|
||||||
for key in nios_provider_spec.keys():
|
for key, value in iteritems(nios_provider_spec):
|
||||||
if key not in kwargs:
|
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()
|
env = ('INFOBLOX_%s' % key).upper()
|
||||||
if env in os.environ:
|
if env in os.environ:
|
||||||
kwargs[key] = os.environ.get(env)
|
kwargs[key] = os.environ.get(env)
|
||||||
|
@ -115,14 +126,10 @@ def flatten_extattrs(value):
|
||||||
class WapiBase(object):
|
class WapiBase(object):
|
||||||
''' Base class for implementing Infoblox WAPI API '''
|
''' Base class for implementing Infoblox WAPI API '''
|
||||||
|
|
||||||
def __init__(self, module):
|
provider_spec = {'provider': dict(type='dict', options=nios_provider_spec)}
|
||||||
self.module = module
|
|
||||||
|
|
||||||
try:
|
def __init__(self, provider):
|
||||||
provider = module.params['provider'] or {}
|
|
||||||
self.connector = get_connector(**provider)
|
self.connector = get_connector(**provider)
|
||||||
except Exception as exc:
|
|
||||||
module.fail_json(msg=to_text(exc))
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
try:
|
try:
|
||||||
|
@ -137,20 +144,50 @@ class WapiBase(object):
|
||||||
method = getattr(self.connector, name)
|
method = getattr(self.connector, name)
|
||||||
return method(*args, **kwargs)
|
return method(*args, **kwargs)
|
||||||
except InfobloxException as exc:
|
except InfobloxException as exc:
|
||||||
|
if hasattr(self, 'handle_exception'):
|
||||||
|
self.handle_exception(name, exc)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
class WapiLookup(WapiBase):
|
||||||
|
''' Implements WapiBase for lookup plugins '''
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
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. This method will then
|
||||||
|
gracefully fail the module.
|
||||||
|
|
||||||
|
:args exc: instance of InfobloxException
|
||||||
|
'''
|
||||||
self.module.fail_json(
|
self.module.fail_json(
|
||||||
msg=exc.response['text'],
|
msg=exc.response['text'],
|
||||||
type=exc.response['Error'].split(':')[0],
|
type=exc.response['Error'].split(':')[0],
|
||||||
code=exc.response.get('code'),
|
code=exc.response.get('code'),
|
||||||
action=name
|
operation=method_name
|
||||||
)
|
)
|
||||||
|
|
||||||
def run(self, ib_obj_type, ib_spec):
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
|
|
||||||
class Wapi(WapiBase):
|
|
||||||
''' Implements WapiBase for executing a NIOS module '''
|
|
||||||
|
|
||||||
def run(self, ib_obj_type, ib_spec):
|
def run(self, ib_obj_type, ib_spec):
|
||||||
''' Runs the module and performans configuration tasks
|
''' Runs the module and performans configuration tasks
|
||||||
|
|
||||||
|
|
|
@ -96,7 +96,7 @@ EXAMPLES = '''
|
||||||
RETURN = ''' # '''
|
RETURN = ''' # '''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.net_tools.nios.api import get_provider_spec, Wapi
|
from ansible.module_utils.net_tools.nios.api import WapiModule
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
@ -116,12 +116,12 @@ def main():
|
||||||
)
|
)
|
||||||
|
|
||||||
argument_spec.update(ib_spec)
|
argument_spec.update(ib_spec)
|
||||||
argument_spec.update(get_provider_spec())
|
argument_spec.update(WapiModule.provider_spec)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
wapi = Wapi(module)
|
wapi = WapiModule(module)
|
||||||
result = wapi.run('view', ib_spec)
|
result = wapi.run('view', ib_spec)
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
|
@ -141,7 +141,7 @@ RETURN = ''' # '''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.six import iteritems
|
from ansible.module_utils.six import iteritems
|
||||||
from ansible.module_utils.net_tools.nios.api import get_provider_spec, Wapi
|
from ansible.module_utils.net_tools.nios.api import WapiModule
|
||||||
|
|
||||||
|
|
||||||
def ipaddr(module, key, filtered_keys=None):
|
def ipaddr(module, key, filtered_keys=None):
|
||||||
|
@ -204,12 +204,12 @@ def main():
|
||||||
)
|
)
|
||||||
|
|
||||||
argument_spec.update(ib_spec)
|
argument_spec.update(ib_spec)
|
||||||
argument_spec.update(get_provider_spec())
|
argument_spec.update(WapiModule.provider_spec)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
wapi = Wapi(module)
|
wapi = WapiModule(module)
|
||||||
result = wapi.run('record:host', ib_spec)
|
result = wapi.run('record:host', ib_spec)
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
|
@ -138,7 +138,7 @@ RETURN = ''' # '''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.six import iteritems
|
from ansible.module_utils.six import iteritems
|
||||||
from ansible.module_utils.net_tools.nios.api import get_provider_spec, Wapi
|
from ansible.module_utils.net_tools.nios.api import WapiModule
|
||||||
|
|
||||||
|
|
||||||
def options(module):
|
def options(module):
|
||||||
|
@ -200,12 +200,12 @@ def main():
|
||||||
)
|
)
|
||||||
|
|
||||||
argument_spec.update(ib_spec)
|
argument_spec.update(ib_spec)
|
||||||
argument_spec.update(get_provider_spec())
|
argument_spec.update(WapiModule.provider_spec)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
wapi = Wapi(module)
|
wapi = WapiModule(module)
|
||||||
result = wapi.run('network', ib_spec)
|
result = wapi.run('network', ib_spec)
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
|
@ -91,7 +91,7 @@ EXAMPLES = '''
|
||||||
RETURN = ''' # '''
|
RETURN = ''' # '''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.net_tools.nios.api import get_provider_spec, Wapi
|
from ansible.module_utils.net_tools.nios.api import WapiModule
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
@ -110,12 +110,12 @@ def main():
|
||||||
)
|
)
|
||||||
|
|
||||||
argument_spec.update(ib_spec)
|
argument_spec.update(ib_spec)
|
||||||
argument_spec.update(get_provider_spec())
|
argument_spec.update(WapiModule.provider_spec)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
wapi = Wapi(module)
|
wapi = WapiModule(module)
|
||||||
result = wapi.run('networkview', ib_spec)
|
result = wapi.run('networkview', ib_spec)
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
|
@ -128,7 +128,7 @@ EXAMPLES = '''
|
||||||
RETURN = ''' # '''
|
RETURN = ''' # '''
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
from ansible.module_utils.net_tools.nios.api import get_provider_spec, Wapi
|
from ansible.module_utils.net_tools.nios.api import WapiModule
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
@ -157,12 +157,12 @@ def main():
|
||||||
)
|
)
|
||||||
|
|
||||||
argument_spec.update(ib_spec)
|
argument_spec.update(ib_spec)
|
||||||
argument_spec.update(get_provider_spec())
|
argument_spec.update(WapiModule.provider_spec)
|
||||||
|
|
||||||
module = AnsibleModule(argument_spec=argument_spec,
|
module = AnsibleModule(argument_spec=argument_spec,
|
||||||
supports_check_mode=True)
|
supports_check_mode=True)
|
||||||
|
|
||||||
wapi = Wapi(module)
|
wapi = WapiModule(module)
|
||||||
result = wapi.run('zone_auth', ib_spec)
|
result = wapi.run('zone_auth', ib_spec)
|
||||||
|
|
||||||
module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
|
@ -95,8 +95,7 @@ obj_type:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ansible.plugins.lookup import LookupBase
|
from ansible.plugins.lookup import LookupBase
|
||||||
from ansible.module_utils.net_tools.nios.api import nios_provider_spec
|
from ansible.module_utils.net_tools.nios.api import WapiLookup
|
||||||
from ansible.module_utils.net_tools.nios.api import get_connector
|
|
||||||
from ansible.module_utils.net_tools.nios.api import normalize_extattrs, flatten_extattrs
|
from ansible.module_utils.net_tools.nios.api import normalize_extattrs, flatten_extattrs
|
||||||
from ansible.errors import AnsibleError
|
from ansible.errors import AnsibleError
|
||||||
|
|
||||||
|
@ -113,8 +112,8 @@ class LookupModule(LookupBase):
|
||||||
filter_data = kwargs.pop('filter', {})
|
filter_data = kwargs.pop('filter', {})
|
||||||
extattrs = normalize_extattrs(kwargs.pop('extattrs', {}))
|
extattrs = normalize_extattrs(kwargs.pop('extattrs', {}))
|
||||||
provider = kwargs.pop('provider', {})
|
provider = kwargs.pop('provider', {})
|
||||||
connector = get_connector(**provider)
|
wapi = WapiLookup(provider)
|
||||||
res = connector.get_object(obj_type, filter_data, return_fields=return_fields)
|
res = wapi.get_object(obj_type, filter_data, return_fields=return_fields)
|
||||||
for obj in res:
|
for obj in res:
|
||||||
if 'extattrs' in obj:
|
if 'extattrs' in obj:
|
||||||
obj['extattrs'] = flatten_extattrs(obj['extattrs'])
|
obj['extattrs'] = flatten_extattrs(obj['extattrs'])
|
||||||
|
|
|
@ -60,8 +60,7 @@ _list:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ansible.plugins.lookup import LookupBase
|
from ansible.plugins.lookup import LookupBase
|
||||||
from ansible.module_utils.net_tools.nios.api import nios_provider_spec
|
from ansible.module_utils.net_tools.nios.api import WapiLookup
|
||||||
from ansible.module_utils.net_tools.nios.api import get_connector
|
|
||||||
from ansible.module_utils._text import to_text
|
from ansible.module_utils._text import to_text
|
||||||
from ansible.errors import AnsibleError
|
from ansible.errors import AnsibleError
|
||||||
|
|
||||||
|
@ -75,9 +74,9 @@ class LookupModule(LookupBase):
|
||||||
raise AnsibleError('missing argument in the form of A.B.C.D/E')
|
raise AnsibleError('missing argument in the form of A.B.C.D/E')
|
||||||
|
|
||||||
provider = kwargs.pop('provider', {})
|
provider = kwargs.pop('provider', {})
|
||||||
connector = get_connector(**provider)
|
wapi = WapiLookup(provider)
|
||||||
|
|
||||||
network_obj = connector.get_object('network', {'network': network})
|
network_obj = wapi.get_object('network', {'network': network})
|
||||||
if network_obj is None:
|
if network_obj is None:
|
||||||
raise AnsibleError('unable to find network object %s' % network)
|
raise AnsibleError('unable to find network object %s' % network)
|
||||||
|
|
||||||
|
@ -85,7 +84,7 @@ class LookupModule(LookupBase):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ref = network_obj[0]['_ref']
|
ref = network_obj[0]['_ref']
|
||||||
avail_ips = connector.call_func('next_available_ip', ref, {'num': num})
|
avail_ips = wapi.call_func('next_available_ip', ref, {'num': num})
|
||||||
return [avail_ips['ips']]
|
return [avail_ips['ips']]
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise AnsibleError(to_text(exc))
|
raise AnsibleError(to_text(exc))
|
||||||
|
|
|
@ -30,24 +30,18 @@ class TestNiosApi(unittest.TestCase):
|
||||||
self.mock_connector.stop()
|
self.mock_connector.stop()
|
||||||
|
|
||||||
def test_get_provider_spec(self):
|
def test_get_provider_spec(self):
|
||||||
provider_options = ['host', 'username', 'password', 'ssl_verify',
|
provider_options = ['host', 'username', 'password', 'ssl_verify', 'silent_ssl_warnings',
|
||||||
'http_request_timeout', 'http_pool_connections',
|
'http_request_timeout', 'http_pool_connections',
|
||||||
'http_pool_maxsize', 'max_retries', 'wapi_version']
|
'http_pool_maxsize', 'max_retries', 'wapi_version']
|
||||||
res = api.get_provider_spec()
|
res = api.WapiBase.provider_spec
|
||||||
self.assertIsNotNone(res)
|
self.assertIsNotNone(res)
|
||||||
self.assertIn('provider', res)
|
self.assertIn('provider', res)
|
||||||
self.assertIn('options', res['provider'])
|
self.assertIn('options', res['provider'])
|
||||||
returned_options = res['provider']['options']
|
returned_options = res['provider']['options']
|
||||||
self.assertEqual(sorted(provider_options), sorted(returned_options.keys()))
|
self.assertEqual(sorted(provider_options), sorted(returned_options.keys()))
|
||||||
|
|
||||||
def test_wapi_base(self):
|
|
||||||
wapi = api.WapiBase(self.module)
|
|
||||||
|
|
||||||
with self.assertRaises(NotImplementedError):
|
|
||||||
wapi.run(None, None)
|
|
||||||
|
|
||||||
def _get_wapi(self, test_object):
|
def _get_wapi(self, test_object):
|
||||||
wapi = api.Wapi(self.module)
|
wapi = api.WapiModule(self.module)
|
||||||
wapi.get_object = Mock(name='get_object', return_value=test_object)
|
wapi.get_object = Mock(name='get_object', return_value=test_object)
|
||||||
wapi.create_object = Mock(name='create_object')
|
wapi.create_object = Mock(name='create_object')
|
||||||
wapi.update_object = Mock(name='update_object')
|
wapi.update_object = Mock(name='update_object')
|
||||||
|
|
Loading…
Reference in a new issue