Adds refactors for more f5 modules (#34824)

The main patch is to remove the traceback generating code. There are
other small fixes that were made in addition to doing that.

* Removed re-def of cleanup_tokens.
* Changed parameter args to be keywords.
* Changed imports to include new module_util locations.
* Imports also include developing (sideband) module_util locations.
* Changed to using F5Client and plain AnsibleModule to prevent tracebacks caused by missing libraries.
* Removed init and update methods from most Parameter classes (optimization) as its now included in module_utils.
* Changed module and module param references to take into account the new self.module arg.
* Minor bug fixes made during this refactor.
pull/34830/head
Tim Rupp 7 years ago committed by GitHub
parent 0e4e7de000
commit a10aee0fc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -110,11 +110,9 @@ options:
version_added: 2.5 version_added: 2.5
notes: notes:
- Requires BIG-IP software version >= 12. - Requires BIG-IP software version >= 12.
- F5 developed module 'F5-SDK' required (https://github.com/F5Networks/f5-common-python). - To add members do a pool, use the C(bigip_pool_member) module. Previously, the
- Best run as a local_action in your playbook. C(bigip_pool) module allowed the management of users, but this has been removed
requirements: in version 2.5 of Ansible.
- f5-sdk
- Python >= 2.7
extends_documentation_fragment: f5 extends_documentation_fragment: f5
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
@ -297,23 +295,45 @@ metadata:
import re import re
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.f5_utils import AnsibleF5Parameters from ansible.module_utils.basic import env_fallback
from ansible.module_utils.f5_utils import HAS_F5SDK
from ansible.module_utils.f5_utils import F5ModuleError
from ansible.module_utils.six import iteritems from ansible.module_utils.six import iteritems
from collections import defaultdict
HAS_DEVEL_IMPORTS = False
try: try:
from netaddr import IPAddress, AddrFormatError # Sideband repository used for dev
HAS_NETADDR = True from library.module_utils.network.f5.bigip import HAS_F5SDK
from library.module_utils.network.f5.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import fqdn_name
from library.module_utils.network.f5.common import f5_argument_spec
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
HAS_DEVEL_IMPORTS = True
except ImportError: except ImportError:
HAS_NETADDR = False # Upstream Ansible
from ansible.module_utils.network.f5.bigip import HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client
from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters
from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
try: try:
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from netaddr import IPAddress, AddrFormatError
HAS_NETADDR = True
except ImportError: except ImportError:
HAS_F5SDK = False HAS_NETADDR = False
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
@ -342,35 +362,6 @@ class Parameters(AnsibleF5Parameters):
'metadata' 'metadata'
] ]
def __init__(self, params=None):
self._values = defaultdict(lambda: None)
if params:
self.update(params=params)
self._values['__warnings'] = []
def update(self, params=None):
if params:
for k, v in iteritems(params):
if self.api_map is not None and k in self.api_map:
map_key = self.api_map[k]
else:
map_key = k
# Handle weird API parameters like `dns.proxy.__iter__` by
# using a map provided by the module developer
class_attr = getattr(type(self), map_key, None)
if isinstance(class_attr, property):
# There is a mapped value for the api_map key
if class_attr.fset is None:
# If the mapped value does not have an associated setter
self._values[map_key] = v
else:
# The mapped value has a setter
setattr(self, map_key, v)
else:
# If the mapped value is not a @property
self._values[map_key] = v
@property @property
def lb_method(self): def lb_method(self):
lb_method = self._values['lb_method'] lb_method = self._values['lb_method']
@ -399,18 +390,6 @@ class Parameters(AnsibleF5Parameters):
result = ' and '.join(monitors).strip() result = ' and '.join(monitors).strip()
return result return result
def api_params(self):
result = {}
for api_attribute in self.api_attributes:
if self.api_map is not None and api_attribute in self.api_map:
result[api_attribute] = getattr(
self, self.api_map[api_attribute]
)
else:
result[api_attribute] = getattr(self, api_attribute)
result = self._filter_params(result)
return result
def _verify_quorum_type(self, quorum): def _verify_quorum_type(self, quorum):
try: try:
if quorum is None: if quorum is None:
@ -650,9 +629,10 @@ class Difference(object):
class ModuleManager(object): class ModuleManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.want = ModuleParameters(params=self.client.module.params) self.client = kwargs.get('client', None)
self.want = ModuleParameters(params=self.module.params)
self.have = ApiParameters() self.have = ApiParameters()
self.changes = UsableChanges() self.changes = UsableChanges()
@ -669,7 +649,7 @@ class ModuleManager(object):
except iControlUnexpectedHTTPError as e: except iControlUnexpectedHTTPError as e:
raise F5ModuleError(str(e)) raise F5ModuleError(str(e))
reportable = ReportableChanges(self.changes.to_return()) reportable = ReportableChanges(params=self.changes.to_return())
changes = reportable.to_return() changes = reportable.to_return()
result.update(**changes) result.update(**changes)
result.update(dict(changed=changed)) result.update(dict(changed=changed))
@ -679,7 +659,7 @@ class ModuleManager(object):
def _announce_deprecations(self, result): def _announce_deprecations(self, result):
warnings = result.pop('__warnings', []) warnings = result.pop('__warnings', [])
for warning in warnings: for warning in warnings:
self.client.module.deprecate( self.module.deprecate(
msg=warning['msg'], msg=warning['msg'],
version=warning['version'] version=warning['version']
) )
@ -690,7 +670,7 @@ class ModuleManager(object):
if getattr(self.want, key) is not None: if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key) changed[key] = getattr(self.want, key)
if changed: if changed:
self.changes = UsableChanges(changed) self.changes = UsableChanges(params=changed)
def _update_changed_options(self): def _update_changed_options(self):
diff = Difference(self.want, self.have) diff = Difference(self.want, self.have)
@ -706,7 +686,7 @@ class ModuleManager(object):
else: else:
changed[k] = change changed[k] = change
if changed: if changed:
self.changes = UsableChanges(changed) self.changes = UsableChanges(params=changed)
return True return True
return False return False
@ -731,13 +711,13 @@ class ModuleManager(object):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
if not self.should_update(): if not self.should_update():
return False return False
if self.client.check_mode: if self.module.check_mode:
return True return True
self.update_on_device() self.update_on_device()
return True return True
def remove(self): def remove(self):
if self.client.check_mode: if self.module.check_mode:
return True return True
self.remove_from_device() self.remove_from_device()
if self.exists(): if self.exists():
@ -764,7 +744,7 @@ class ModuleManager(object):
) )
self._set_changed_options() self._set_changed_options()
if self.client.check_mode: if self.module.check_mode:
return True return True
self.create_on_device() self.create_on_device()
return True return True
@ -804,7 +784,7 @@ class ModuleManager(object):
params='expandSubcollections=true' params='expandSubcollections=true'
) )
) )
return ApiParameters(resource.attrs) return ApiParameters(params=resource.attrs)
class ArgumentSpec(object): class ArgumentSpec(object):
@ -831,7 +811,7 @@ class ArgumentSpec(object):
'weighted-least-connections-node' 'weighted-least-connections-node'
] ]
self.supports_check_mode = True self.supports_check_mode = True
self.argument_spec = dict( argument_spec = dict(
name=dict( name=dict(
required=True, required=True,
aliases=['pool'] aliases=['pool']
@ -863,44 +843,42 @@ class ArgumentSpec(object):
] ]
), ),
description=dict(), description=dict(),
metadata=dict(type='raw') metadata=dict(type='raw'),
state=dict(
default='present',
choices=['present', 'absent']
),
partition=dict(
default='Common',
fallback=(env_fallback, ['F5_PARTITION'])
) )
self.f5_product_name = 'bigip'
def cleanup_tokens(client):
try:
resource = client.api.shared.authz.tokens_s.token.load(
name=client.api.icrs.token
) )
resource.delete() self.argument_spec = {}
except Exception: self.argument_spec.update(f5_argument_spec)
pass self.argument_spec.update(argument_spec)
def main(): def main():
if not HAS_F5SDK:
raise F5ModuleError("The python f5-sdk module is required")
if not HAS_NETADDR:
raise F5ModuleError("The python netaddr module is required")
spec = ArgumentSpec() spec = ArgumentSpec()
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=spec.supports_check_mode, supports_check_mode=spec.supports_check_mode
f5_product_name=spec.f5_product_name
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
if not HAS_NETADDR:
module.fail_json(msg="The python netaddr module is required")
try: try:
mm = ModuleManager(client) client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
cleanup_tokens(client) cleanup_tokens(client)
client.module.exit_json(**results) module.exit_json(**results)
except F5ModuleError as e: except F5ModuleError as ex:
cleanup_tokens(client) cleanup_tokens(client)
client.module.fail_json(msg=str(e)) module.fail_json(msg=str(ex))
if __name__ == '__main__': if __name__ == '__main__':

@ -65,15 +65,19 @@ options:
partition: partition:
description: description:
- Device partition to manage resources on. - Device partition to manage resources on.
required: False default: Common
default: 'Common' version_added: 2.5
state:
description:
- When C(present), ensures that the profile exists.
- When C(absent), ensures the profile is removed.
default: present
choices:
- present
- absent
version_added: 2.5 version_added: 2.5
notes: notes:
- Requires the f5-sdk Python package on the host. This is as easy as pip
install f5-sdk.
- Requires BIG-IP software version >= 12 - Requires BIG-IP software version >= 12
requirements:
- f5-sdk >= 2.2.3
extends_documentation_fragment: f5 extends_documentation_fragment: f5
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
@ -123,77 +127,58 @@ ciphers:
import os import os
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.f5_utils import AnsibleF5Parameters from ansible.module_utils.basic import env_fallback
from ansible.module_utils.f5_utils import HAS_F5SDK
from ansible.module_utils.f5_utils import F5ModuleError
from ansible.module_utils.six import iteritems from ansible.module_utils.six import iteritems
from collections import defaultdict
HAS_DEVEL_IMPORTS = False
try: try:
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError # Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK
from library.module_utils.network.f5.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import fqdn_name
from library.module_utils.network.f5.common import f5_argument_spec
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
HAS_DEVEL_IMPORTS = True
except ImportError: except ImportError:
# Upstream Ansible
from ansible.module_utils.network.f5.bigip import HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client
from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters
from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
api_map = { api_map = {
'certKeyChain': 'cert_key_chain', 'certKeyChain': 'cert_key_chain'
'ocspStapling': 'ocsp_stapling'
} }
api_attributes = [ api_attributes = [
'ciphers', 'certKeyChain', 'ocspStapling' 'ciphers', 'certKeyChain'
] ]
returnables = [ returnables = [
'ciphers', 'ocsp_stapling' 'ciphers'
] ]
updatables = [ updatables = [
'ciphers', 'cert_key_chain', 'ocsp_stapling' 'ciphers', 'cert_key_chain'
] ]
def __init__(self, params=None):
self._values = defaultdict(lambda: None)
self._values['__warnings'] = []
if params:
self.update(params=params)
def update(self, params=None):
if params:
for k, v in iteritems(params):
if self.api_map is not None and k in self.api_map:
map_key = self.api_map[k]
else:
map_key = k
# Handle weird API parameters like `dns.proxy.__iter__` by
# using a map provided by the module developer
class_attr = getattr(type(self), map_key, None)
if isinstance(class_attr, property):
# There is a mapped value for the api_map key
if class_attr.fset is None:
# If the mapped value does not have
# an associated setter
self._values[map_key] = v
else:
# The mapped value has a setter
setattr(self, map_key, v)
else:
# If the mapped value is not a @property
self._values[map_key] = v
def api_params(self):
result = {}
for api_attribute in self.api_attributes:
if self.api_map is not None and api_attribute in self.api_map:
result[api_attribute] = getattr(self, self.api_map[api_attribute])
else:
result[api_attribute] = getattr(self, api_attribute)
result = self._filter_params(result)
return result
class ModuleParameters(Parameters): class ModuleParameters(Parameters):
def _fqdn_name(self, value): def _fqdn_name(self, value):
@ -272,8 +257,10 @@ class ApiParameters(Parameters):
for x in ['cert', 'key', 'chain', 'passphrase']: for x in ['cert', 'key', 'chain', 'passphrase']:
if x in item: if x in item:
tmp[x] = item[x] tmp[x] = item[x]
if 'chain' not in item:
tmp['chain'] = 'none'
result.append(tmp) result.append(tmp)
result = sorted(result, key=lambda x: x['name']) result = sorted(result, key=lambda y: y['name'])
return result return result
@ -352,9 +339,10 @@ class Difference(object):
class ModuleManager(object): class ModuleManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.want = ModuleParameters(params=self.client.module.params) self.client = kwargs.get('client', None)
self.want = ModuleParameters(params=self.module.params)
self.have = ApiParameters() self.have = ApiParameters()
self.changes = UsableChanges() self.changes = UsableChanges()
@ -364,7 +352,7 @@ class ModuleManager(object):
if getattr(self.want, key) is not None: if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key) changed[key] = getattr(self.want, key)
if changed: if changed:
self.changes = UsableChanges(changed) self.changes = UsableChanges(params=changed)
def _update_changed_options(self): def _update_changed_options(self):
diff = Difference(self.want, self.have) diff = Difference(self.want, self.have)
@ -380,7 +368,7 @@ class ModuleManager(object):
else: else:
changed[k] = change changed[k] = change
if changed: if changed:
self.changes = UsableChanges(changed) self.changes = UsableChanges(params=changed)
return True return True
return False return False
@ -397,7 +385,7 @@ class ModuleManager(object):
except iControlUnexpectedHTTPError as e: except iControlUnexpectedHTTPError as e:
raise F5ModuleError(str(e)) raise F5ModuleError(str(e))
reportable = ReportableChanges(self.changes.to_return()) reportable = ReportableChanges(params=self.changes.to_return())
changes = reportable.to_return() changes = reportable.to_return()
result.update(**changes) result.update(**changes)
result.update(dict(changed=changed)) result.update(dict(changed=changed))
@ -407,7 +395,7 @@ class ModuleManager(object):
def _announce_deprecations(self, result): def _announce_deprecations(self, result):
warnings = result.pop('__warnings', []) warnings = result.pop('__warnings', [])
for warning in warnings: for warning in warnings:
self.client.module.deprecate( self.module.deprecate(
msg=warning['msg'], msg=warning['msg'],
version=warning['version'] version=warning['version']
) )
@ -422,7 +410,7 @@ class ModuleManager(object):
self._set_changed_options() self._set_changed_options()
if self.want.ciphers is None: if self.want.ciphers is None:
self.want.update({'ciphers': 'DEFAULT'}) self.want.update({'ciphers': 'DEFAULT'})
if self.client.check_mode: if self.module.check_mode:
return True return True
self.create_on_device() self.create_on_device()
return True return True
@ -437,7 +425,7 @@ class ModuleManager(object):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
if not self.should_update(): if not self.should_update():
return False return False
if self.client.check_mode: if self.module.check_mode:
return True return True
self.update_on_device() self.update_on_device()
return True return True
@ -448,7 +436,7 @@ class ModuleManager(object):
return False return False
def remove(self): def remove(self):
if self.client.check_mode: if self.module.check_mode:
return True return True
self.remove_from_device() self.remove_from_device()
if self.exists(): if self.exists():
@ -461,7 +449,7 @@ class ModuleManager(object):
partition=self.want.partition partition=self.want.partition
) )
result = resource.attrs result = resource.attrs
return ApiParameters(result) return ApiParameters(params=result)
def exists(self): def exists(self):
result = self.client.api.tm.ltm.profile.client_ssls.client_ssl.exists( result = self.client.api.tm.ltm.profile.client_ssls.client_ssl.exists(
@ -498,7 +486,7 @@ class ModuleManager(object):
class ArgumentSpec(object): class ArgumentSpec(object):
def __init__(self): def __init__(self):
self.supports_check_mode = True self.supports_check_mode = True
self.argument_spec = dict( argument_spec = dict(
name=dict(required=True), name=dict(required=True),
parent=dict(), parent=dict(),
ciphers=dict(), ciphers=dict(),
@ -510,41 +498,40 @@ class ArgumentSpec(object):
chain=dict(), chain=dict(),
passphrase=dict() passphrase=dict()
) )
),
state=dict(
default='present',
choices=['present', 'absent']
),
partition=dict(
default='Common',
fallback=(env_fallback, ['F5_PARTITION'])
) )
) )
self.f5_product_name = 'bigip' self.argument_spec = {}
self.argument_spec.update(f5_argument_spec)
self.argument_spec.update(argument_spec)
def cleanup_tokens(client):
try:
resource = client.api.shared.authz.tokens_s.token.load(
name=client.api.icrs.token
)
resource.delete()
except Exception:
pass
def main(): def main():
if not HAS_F5SDK:
raise F5ModuleError("The python f5-sdk module is required")
spec = ArgumentSpec() spec = ArgumentSpec()
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=spec.supports_check_mode, supports_check_mode=spec.supports_check_mode
f5_product_name=spec.f5_product_name
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
try: try:
mm = ModuleManager(client) client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
cleanup_tokens(client) cleanup_tokens(client)
client.module.exit_json(**results) module.exit_json(**results)
except F5ModuleError as e: except F5ModuleError as ex:
cleanup_tokens(client) cleanup_tokens(client)
client.module.fail_json(msg=str(e)) module.fail_json(msg=str(ex))
if __name__ == '__main__': if __name__ == '__main__':

@ -65,11 +65,6 @@ options:
choices: choices:
- present - present
- absent - absent
notes:
- Requires the f5-sdk Python package on the host. This is as easy as pip
install f5-sdk.
requirements:
- f5-sdk >= 2.2.3
extends_documentation_fragment: f5 extends_documentation_fragment: f5
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
@ -107,15 +102,41 @@ level:
import time import time
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.f5_utils import AnsibleF5Parameters
from ansible.module_utils.f5_utils import HAS_F5SDK HAS_DEVEL_IMPORTS = False
from ansible.module_utils.f5_utils import F5ModuleError
try:
# Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK
from library.module_utils.network.f5.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import fqdn_name
from library.module_utils.network.f5.common import f5_argument_spec
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
HAS_DEVEL_IMPORTS = True
except ImportError:
# Upstream Ansible
from ansible.module_utils.network.f5.bigip import HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client
from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters
from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
try: try:
from f5.bigip.contexts import TransactionContextManager from f5.bigip.contexts import TransactionContextManager
from f5.sdk_exception import LazyAttributesRequired from f5.sdk_exception import LazyAttributesRequired
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError
except ImportError: except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
@ -137,16 +158,6 @@ class Parameters(AnsibleF5Parameters):
except Exception: except Exception:
return result return result
def api_params(self):
result = {}
for api_attribute in self.api_attributes:
if self.api_map is not None and api_attribute in self.api_map:
result[api_attribute] = getattr(self, self.api_map[api_attribute])
else:
result[api_attribute] = getattr(self, api_attribute)
result = self._filter_params(result)
return result
@property @property
def level(self): def level(self):
if self._values['level'] is None: if self._values['level'] is None:
@ -155,10 +166,11 @@ class Parameters(AnsibleF5Parameters):
class ModuleManager(object): class ModuleManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None)
self.have = None self.have = None
self.want = Parameters(self.client.module.params) self.want = Parameters(params=self.module.params)
self.changes = Parameters() self.changes = Parameters()
def _update_changed_options(self): def _update_changed_options(self):
@ -170,7 +182,7 @@ class ModuleManager(object):
if attr1 != attr2: if attr1 != attr2:
changed[key] = attr1 changed[key] = attr1
if changed: if changed:
self.changes = Parameters(changed) self.changes = Parameters(params=changed)
return True return True
return False return False
@ -208,7 +220,7 @@ class ModuleManager(object):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
if not self.should_update(): if not self.should_update():
return False return False
if self.client.check_mode: if self.module.check_mode:
return True return True
self.update_on_device() self.update_on_device()
@ -261,7 +273,7 @@ class ModuleManager(object):
resource = getattr(provision, str(self.want.module)) resource = getattr(provision, str(self.want.module))
resource = resource.load() resource = resource.load()
result = resource.attrs result = resource.attrs
return Parameters(result) return Parameters(params=result)
def absent(self): def absent(self):
if self.exists(): if self.exists():
@ -269,7 +281,7 @@ class ModuleManager(object):
return False return False
def remove(self): def remove(self):
if self.client.check_mode: if self.module.check_mode:
return True return True
self.remove_from_device() self.remove_from_device()
self._wait_for_module_provisioning() self._wait_for_module_provisioning()
@ -409,7 +421,7 @@ class ModuleManager(object):
class ArgumentSpec(object): class ArgumentSpec(object):
def __init__(self): def __init__(self):
self.supports_check_mode = True self.supports_check_mode = True
self.argument_spec = dict( argument_spec = dict(
module=dict( module=dict(
required=True, required=True,
choices=[ choices=[
@ -428,31 +440,34 @@ class ArgumentSpec(object):
choices=['present', 'absent'] choices=['present', 'absent']
) )
) )
self.argument_spec = {}
self.argument_spec.update(f5_argument_spec)
self.argument_spec.update(argument_spec)
self.mutually_exclusive = [ self.mutually_exclusive = [
['parameters', 'parameters_src'] ['parameters', 'parameters_src']
] ]
self.f5_product_name = 'bigip'
def main(): def main():
if not HAS_F5SDK:
raise F5ModuleError("The python f5-sdk module is required")
spec = ArgumentSpec() spec = ArgumentSpec()
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
mutually_exclusive=spec.mutually_exclusive,
supports_check_mode=spec.supports_check_mode, supports_check_mode=spec.supports_check_mode,
f5_product_name=spec.f5_product_name mutually_exclusive=spec.mutually_exclusive
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
try: try:
mm = ModuleManager(client) client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
client.module.exit_json(**results) cleanup_tokens(client)
except F5ModuleError as e: module.exit_json(**results)
client.module.fail_json(msg=str(e)) except F5ModuleError as ex:
cleanup_tokens(client)
module.fail_json(msg=str(ex))
if __name__ == '__main__': if __name__ == '__main__':

@ -37,9 +37,7 @@ options:
- When C(True), includes the ASM request log data. When C(False), - When C(True), includes the ASM request log data. When C(False),
excludes the ASM request log data. excludes the ASM request log data.
default: no default: no
choices: type: bool
- yes
- no
max_file_size: max_file_size:
description: description:
- Max file size, in bytes, of the qkview to create. By default, no max - Max file size, in bytes, of the qkview to create. By default, no max
@ -49,16 +47,12 @@ options:
description: description:
- Include complete information in the qkview. - Include complete information in the qkview.
default: yes default: yes
choices: type: bool
- yes
- no
exclude_core: exclude_core:
description: description:
- Exclude core files from the qkview. - Exclude core files from the qkview.
default: no default: no
choices: type: bool
- yes
- no
exclude: exclude:
description: description:
- Exclude various file from the qkview. - Exclude various file from the qkview.
@ -72,15 +66,9 @@ options:
- If C(no), the file will only be transferred if the destination does not - If C(no), the file will only be transferred if the destination does not
exist. exist.
default: yes default: yes
choices: type: bool
- yes
- no
notes: notes:
- Requires the f5-sdk Python package on the host. This is as easy as pip
install f5-sdk.
- This module does not include the "max time" or "restrict to blade" options. - This module does not include the "max time" or "restrict to blade" options.
requirements:
- f5-sdk >= 2.2.3
extends_documentation_fragment: f5 extends_documentation_fragment: f5
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
@ -110,19 +98,41 @@ stdout_lines:
sample: [['...', '...'], ['...'], ['...']] sample: [['...', '...'], ['...'], ['...']]
''' '''
import re
import os import os
import re
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six import string_types from ansible.module_utils.six import string_types
from ansible.module_utils.f5_utils import AnsibleF5Client
from ansible.module_utils.f5_utils import AnsibleF5Parameters
from ansible.module_utils.f5_utils import HAS_F5SDK
from ansible.module_utils.f5_utils import F5ModuleError
from distutils.version import LooseVersion from distutils.version import LooseVersion
HAS_DEVEL_IMPORTS = False
try: try:
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError # Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK
from library.module_utils.network.f5.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import fqdn_name
from library.module_utils.network.f5.common import f5_argument_spec
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
HAS_DEVEL_IMPORTS = True
except ImportError: except ImportError:
# Upstream Ansible
from ansible.module_utils.network.f5.bigip import HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client
from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters
from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
@ -207,8 +217,10 @@ class Parameters(AnsibleF5Parameters):
class ModuleManager(object): class ModuleManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None)
self.kwargs = kwargs
def exec_module(self): def exec_module(self):
if self.is_version_less_than_14(): if self.is_version_less_than_14():
@ -219,9 +231,9 @@ class ModuleManager(object):
def get_manager(self, type): def get_manager(self, type):
if type == 'madm': if type == 'madm':
return MadmLocationManager(self.client) return MadmLocationManager(**self.kwargs)
elif type == 'bulk': elif type == 'bulk':
return BulkLocationManager(self.client) return BulkLocationManager(**self.kwargs)
def is_version_less_than_14(self): def is_version_less_than_14(self):
"""Checks to see if the TMOS version is less than 14 """Checks to see if the TMOS version is less than 14
@ -239,9 +251,11 @@ class ModuleManager(object):
class BaseManager(object): class BaseManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.want = Parameters(self.client.module.params) self.client = kwargs.get('client', None)
self.have = None
self.want = Parameters(params=self.module.params)
self.changes = Parameters() self.changes = Parameters()
def _set_changed_options(self): def _set_changed_options(self):
@ -250,7 +264,7 @@ class BaseManager(object):
if getattr(self.want, key) is not None: if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key) changed[key] = getattr(self.want, key)
if changed: if changed:
self.changes = Parameters(changed) self.changes = Parameters(params=changed)
def _to_lines(self, stdout): def _to_lines(self, stdout):
lines = [] lines = []
@ -342,8 +356,8 @@ class BaseManager(object):
class BulkLocationManager(BaseManager): class BulkLocationManager(BaseManager):
def __init__(self, client): def __init__(self, *args, **kwargs):
super(BulkLocationManager, self).__init__(client) super(BulkLocationManager, self).__init__(**kwargs)
self.remote_dir = '/var/config/rest/bulk' self.remote_dir = '/var/config/rest/bulk'
def _move_qkview_to_download(self): def _move_qkview_to_download(self):
@ -368,8 +382,8 @@ class BulkLocationManager(BaseManager):
class MadmLocationManager(BaseManager): class MadmLocationManager(BaseManager):
def __init__(self, client): def __init__(self, *args, **kwargs):
super(MadmLocationManager, self).__init__(client) super(MadmLocationManager, self).__init__(**kwargs)
self.remote_dir = '/var/config/rest/madm' self.remote_dir = '/var/config/rest/madm'
def _move_qkview_to_download(self): def _move_qkview_to_download(self):
@ -396,7 +410,7 @@ class MadmLocationManager(BaseManager):
class ArgumentSpec(object): class ArgumentSpec(object):
def __init__(self): def __init__(self):
self.supports_check_mode = True self.supports_check_mode = True
self.argument_spec = dict( argument_spec = dict(
filename=dict( filename=dict(
default='localhost.localdomain.qkview' default='localhost.localdomain.qkview'
), ),
@ -427,27 +441,30 @@ class ArgumentSpec(object):
required=True required=True
) )
) )
self.f5_product_name = 'bigip' self.argument_spec = {}
self.argument_spec.update(f5_argument_spec)
self.argument_spec.update(argument_spec)
def main(): def main():
if not HAS_F5SDK:
raise F5ModuleError("The python f5-sdk module is required")
spec = ArgumentSpec() spec = ArgumentSpec()
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=spec.supports_check_mode, supports_check_mode=spec.supports_check_mode
f5_product_name=spec.f5_product_name
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
try: try:
mm = ModuleManager(client) client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
client.module.exit_json(**results) cleanup_tokens(client)
except F5ModuleError as e: module.exit_json(**results)
client.module.fail_json(msg=str(e)) except F5ModuleError as ex:
cleanup_tokens(client)
module.fail_json(msg=str(ex))
if __name__ == '__main__': if __name__ == '__main__':

@ -36,13 +36,10 @@ options:
remote syslog, if this parameter is not specified, the default value remote syslog, if this parameter is not specified, the default value
C(none) is used. C(none) is used.
notes: notes:
- Requires the f5-sdk Python package on the host. This is as easy as pip
install f5-sdk.
- Requires the netaddr Python package on the host. This is as easy as pip - Requires the netaddr Python package on the host. This is as easy as pip
install netaddr. install netaddr.
extends_documentation_fragment: f5 extends_documentation_fragment: f5
requirements: requirements:
- f5-sdk >= 2.2.0
- netaddr - netaddr
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
@ -84,24 +81,45 @@ local_ip:
import re import re
try: from ansible.module_utils.basic import AnsibleModule
import netaddr
HAS_NETADDR = True
except ImportError:
HAS_NETADDR = False
from ansible.module_utils.f5_utils import AnsibleF5Client
from ansible.module_utils.f5_utils import AnsibleF5Parameters
from ansible.module_utils.f5_utils import F5ModuleError
from ansible.module_utils.six import iteritems from ansible.module_utils.six import iteritems
from collections import defaultdict
HAS_DEVEL_IMPORTS = False
try: try:
from ansible.module_utils.f5_utils import HAS_F5SDK # Sideband repository used for dev
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from library.module_utils.network.f5.bigip import HAS_F5SDK
from library.module_utils.network.f5.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import fqdn_name
from library.module_utils.network.f5.common import f5_argument_spec
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
HAS_DEVEL_IMPORTS = True
except ImportError: except ImportError:
# Upstream Ansible
from ansible.module_utils.network.f5.bigip import HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client
from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters
from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
try:
import netaddr
HAS_NETADDR = True
except ImportError:
HAS_NETADDR = False
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
updatables = [ updatables = [
@ -116,36 +134,6 @@ class Parameters(AnsibleF5Parameters):
'remoteServers' 'remoteServers'
] ]
def __init__(self, params=None):
self._values = defaultdict(lambda: None)
self._values['__warnings'] = []
if params:
self.update(params=params)
def update(self, params=None):
if params:
for k, v in iteritems(params):
if self.api_map is not None and k in self.api_map:
map_key = self.api_map[k]
else:
map_key = k
# Handle weird API parameters like `dns.proxy.__iter__` by
# using a map provided by the module developer
class_attr = getattr(type(self), map_key, None)
if isinstance(class_attr, property):
# There is a mapped value for the api_map key
if class_attr.fset is None:
# If the mapped value does not have
# an associated setter
self._values[map_key] = v
else:
# The mapped value has a setter
setattr(self, map_key, v)
else:
# If the mapped value is not a @property
self._values[map_key] = v
def to_return(self): def to_return(self):
result = {} result = {}
for returnable in self.returnables: for returnable in self.returnables:
@ -153,16 +141,6 @@ class Parameters(AnsibleF5Parameters):
result = self._filter_params(result) result = self._filter_params(result)
return result return result
def api_params(self):
result = {}
for api_attribute in self.api_attributes:
if self.api_map is not None and api_attribute in self.api_map:
result[api_attribute] = getattr(self, self.api_map[api_attribute])
else:
result[api_attribute] = getattr(self, api_attribute)
result = self._filter_params(result)
return result
@property @property
def remote_host(self): def remote_host(self):
try: try:
@ -325,10 +303,11 @@ class Difference(object):
class ModuleManager(object): class ModuleManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None)
self.have = None self.have = None
self.want = Parameters(self.client.module.params) self.want = Parameters(params=self.module.params)
self.changes = Changes() self.changes = Changes()
def _set_changed_options(self): def _set_changed_options(self):
@ -337,7 +316,7 @@ class ModuleManager(object):
if getattr(self.want, key) is not None: if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key) changed[key] = getattr(self.want, key)
if changed: if changed:
self.changes = Changes(changed) self.changes = Changes(params=changed)
self.changes.update({'remote_host': self.want.remote_host}) self.changes.update({'remote_host': self.want.remote_host})
def _update_changed_options(self): def _update_changed_options(self):
@ -354,7 +333,7 @@ class ModuleManager(object):
else: else:
changed[k] = change changed[k] = change
if changed: if changed:
self.changes = Changes(changed) self.changes = Changes(params=changed)
self.changes.update({'remote_host': self.want.remote_host}) self.changes.update({'remote_host': self.want.remote_host})
return True return True
return False return False
@ -386,7 +365,7 @@ class ModuleManager(object):
def create(self): def create(self):
self._set_valid_defaults() self._set_valid_defaults()
self._update_changed_options() self._update_changed_options()
if self.client.check_mode: if self.module.check_mode:
return True return True
# This is an unnamed resource, so we only need to update # This is an unnamed resource, so we only need to update
@ -418,7 +397,7 @@ class ModuleManager(object):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
if not self.should_update(): if not self.should_update():
return False return False
if self.client.check_mode: if self.module.check_mode:
return True return True
self.update_on_device() self.update_on_device()
return True return True
@ -449,7 +428,7 @@ class ModuleManager(object):
return False return False
def remove(self): def remove(self):
if self.client.check_mode: if self.module.check_mode:
return True return True
self.remove_from_device() self.remove_from_device()
if self.exists(): if self.exists():
@ -466,7 +445,7 @@ class ModuleManager(object):
class ArgumentSpec(object): class ArgumentSpec(object):
def __init__(self): def __init__(self):
self.supports_check_mode = True self.supports_check_mode = True
self.argument_spec = dict( argument_spec = dict(
remote_host=dict( remote_host=dict(
required=True required=True
), ),
@ -477,42 +456,32 @@ class ArgumentSpec(object):
choices=['absent', 'present'] choices=['absent', 'present']
) )
) )
self.f5_product_name = 'bigip' self.argument_spec = {}
self.argument_spec.update(f5_argument_spec)
self.argument_spec.update(argument_spec)
def cleanup_tokens(client):
try:
resource = client.api.shared.authz.tokens_s.token.load(
name=client.api.icrs.token
)
resource.delete()
except Exception:
pass
def main(): def main():
if not HAS_F5SDK:
raise F5ModuleError("The python f5-sdk module is required")
if not HAS_NETADDR:
raise F5ModuleError("The python netaddr module is required")
spec = ArgumentSpec() spec = ArgumentSpec()
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=spec.supports_check_mode, supports_check_mode=spec.supports_check_mode
f5_product_name=spec.f5_product_name
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
if not HAS_NETADDR:
module.fail_json(msg="The python netaddr module is required")
try: try:
mm = ModuleManager(client) client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
cleanup_tokens(client) cleanup_tokens(client)
client.module.exit_json(**results) module.exit_json(**results)
except F5ModuleError as e: except F5ModuleError as ex:
cleanup_tokens(client) cleanup_tokens(client)
client.module.fail_json(msg=str(e)) module.fail_json(msg=str(ex))
if __name__ == '__main__': if __name__ == '__main__':

@ -48,11 +48,15 @@ options:
specified in either their fully qualified name (/Common/foo) or their short specified in either their fully qualified name (/Common/foo) or their short
name (foo). If a short name is used, the C(partition) argument will automatically name (foo). If a short name is used, the C(partition) argument will automatically
be prepended to the short name. be prepended to the short name.
notes: state:
- Requires the f5-sdk Python package on the host. This is as easy as pip description:
install f5-sdk. - When C(present), ensures that the address list and entries exists.
requirements: - When C(absent), ensures the address list is removed.
- f5-sdk >= 3.0.4 default: present
choices:
- present
- absent
version_added: 2.5
extends_documentation_fragment: f5 extends_documentation_fragment: f5
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
@ -134,38 +138,59 @@ EXAMPLES = r'''
''' '''
RETURN = r''' RETURN = r'''
description:
description: The new description of the port list.
returned: changed
type: string
sample: My port list
ports: ports:
description: The new list of ports applied to the port list description: The new list of ports applied to the port list.
returned: changed returned: changed
type: list type: list
sample: [80, 443] sample: [80, 443]
port_ranges: port_ranges:
description: The new list of port ranges applied to the port list description: The new list of port ranges applied to the port list.
returned: changed returned: changed
type: list type: list
sample: [80-100, 200-8080] sample: [80-100, 200-8080]
ports:
description: The new list of ports applied to the port list
returned: changed
type: list
sample: [80, 443]
port_lists: port_lists:
description: The new list of port list names applied to the port list description: The new list of port list names applied to the port list.
returned: changed returned: changed
type: list type: list
sample: [/Common/list1, /Common/list2] sample: [/Common/list1, /Common/list2]
''' '''
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.f5_utils import AnsibleF5Parameters from ansible.module_utils.basic import env_fallback
from ansible.module_utils.f5_utils import HAS_F5SDK
from ansible.module_utils.f5_utils import F5ModuleError HAS_DEVEL_IMPORTS = False
from ansible.module_utils.six import iteritems
from collections import defaultdict
try: try:
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError # Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK
from library.module_utils.network.f5.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import fqdn_name
from library.module_utils.network.f5.common import f5_argument_spec
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
HAS_DEVEL_IMPORTS = True
except ImportError: except ImportError:
# Upstream Ansible
from ansible.module_utils.network.f5.bigip import HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client
from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters
from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
@ -186,51 +211,11 @@ class Parameters(AnsibleF5Parameters):
'description', 'ports', 'port_ranges', 'port_lists' 'description', 'ports', 'port_ranges', 'port_lists'
] ]
def __init__(self, params=None):
self._values = defaultdict(lambda: None)
self._values['__warnings'] = []
if params:
self.update(params=params)
def update(self, params=None):
if params:
for k, v in iteritems(params):
if self.api_map is not None and k in self.api_map:
map_key = self.api_map[k]
else:
map_key = k
# Handle weird API parameters like `dns.proxy.__iter__` by
# using a map provided by the module developer
class_attr = getattr(type(self), map_key, None)
if isinstance(class_attr, property):
# There is a mapped value for the api_map key
if class_attr.fset is None:
# If the mapped value does not have
# an associated setter
self._values[map_key] = v
else:
# The mapped value has a setter
setattr(self, map_key, v)
else:
# If the mapped value is not a @property
self._values[map_key] = v
def _fqdn_name(self, value): def _fqdn_name(self, value):
if value is not None and not value.startswith('/'): if value is not None and not value.startswith('/'):
return '/{0}/{1}'.format(self.partition, value) return '/{0}/{1}'.format(self.partition, value)
return value return value
def api_params(self):
result = {}
for api_attribute in self.api_attributes:
if self.api_map is not None and api_attribute in self.api_map:
result[api_attribute] = getattr(self, self.api_map[api_attribute])
else:
result[api_attribute] = getattr(self, api_attribute)
result = self._filter_params(result)
return result
class ApiParameters(Parameters): class ApiParameters(Parameters):
@property @property
@ -335,7 +320,7 @@ class ReportableChanges(Changes):
for item in self._values['ports']: for item in self._values['ports']:
if '-' in item['name']: if '-' in item['name']:
continue continue
result.append(item) result.append(item['name'])
return result return result
@property @property
@ -344,7 +329,7 @@ class ReportableChanges(Changes):
for item in self._values['ports']: for item in self._values['ports']:
if '-' not in item['name']: if '-' not in item['name']:
continue continue
result.append(item) result.append(item['name'])
return result return result
@ -433,9 +418,10 @@ class Difference(object):
class ModuleManager(object): class ModuleManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.want = ModuleParameters(params=self.client.module.params) self.client = kwargs.get('client', None)
self.want = ModuleParameters(params=self.module.params)
self.have = ApiParameters() self.have = ApiParameters()
self.changes = UsableChanges() self.changes = UsableChanges()
@ -445,7 +431,7 @@ class ModuleManager(object):
if getattr(self.want, key) is not None: if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key) changed[key] = getattr(self.want, key)
if changed: if changed:
self.changes = UsableChanges(changed) self.changes = UsableChanges(params=changed)
def _update_changed_options(self): def _update_changed_options(self):
diff = Difference(self.want, self.have) diff = Difference(self.want, self.have)
@ -461,7 +447,7 @@ class ModuleManager(object):
else: else:
changed[k] = change changed[k] = change
if changed: if changed:
self.changes = UsableChanges(changed) self.changes = UsableChanges(params=changed)
return True return True
return False return False
@ -484,7 +470,7 @@ class ModuleManager(object):
except iControlUnexpectedHTTPError as e: except iControlUnexpectedHTTPError as e:
raise F5ModuleError(str(e)) raise F5ModuleError(str(e))
reportable = ReportableChanges(self.changes.to_return()) reportable = ReportableChanges(params=self.changes.to_return())
changes = reportable.to_return() changes = reportable.to_return()
result.update(**changes) result.update(**changes)
result.update(dict(changed=changed)) result.update(dict(changed=changed))
@ -494,7 +480,7 @@ class ModuleManager(object):
def _announce_deprecations(self, result): def _announce_deprecations(self, result):
warnings = result.pop('__warnings', []) warnings = result.pop('__warnings', [])
for warning in warnings: for warning in warnings:
self.client.module.deprecate( self.module.deprecate(
msg=warning['msg'], msg=warning['msg'],
version=warning['version'] version=warning['version']
) )
@ -516,13 +502,13 @@ class ModuleManager(object):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
if not self.should_update(): if not self.should_update():
return False return False
if self.client.check_mode: if self.module.check_mode:
return True return True
self.update_on_device() self.update_on_device()
return True return True
def remove(self): def remove(self):
if self.client.check_mode: if self.module.check_mode:
return True return True
self.remove_from_device() self.remove_from_device()
if self.exists(): if self.exists():
@ -531,7 +517,7 @@ class ModuleManager(object):
def create(self): def create(self):
self._set_changed_options() self._set_changed_options()
if self.client.check_mode: if self.module.check_mode:
return True return True
self.create_on_device() self.create_on_device()
return True return True
@ -571,52 +557,51 @@ class ModuleManager(object):
partition=self.want.partition partition=self.want.partition
) )
result = resource.attrs result = resource.attrs
return ApiParameters(result) return ApiParameters(params=result)
class ArgumentSpec(object): class ArgumentSpec(object):
def __init__(self): def __init__(self):
self.supports_check_mode = True self.supports_check_mode = True
self.argument_spec = dict( argument_spec = dict(
name=dict(required=True), name=dict(required=True),
description=dict(), description=dict(),
ports=dict(type='list'), ports=dict(type='list'),
port_ranges=dict(type='list'), port_ranges=dict(type='list'),
port_lists=dict(type='list') port_lists=dict(type='list'),
partition=dict(
default='Common',
fallback=(env_fallback, ['F5_PARTITION'])
),
state=dict(
default='present',
choices=['present', 'absent']
) )
self.f5_product_name = 'bigip'
def cleanup_tokens(client):
try:
resource = client.api.shared.authz.tokens_s.token.load(
name=client.api.icrs.token
) )
resource.delete() self.argument_spec = {}
except Exception: self.argument_spec.update(f5_argument_spec)
pass self.argument_spec.update(argument_spec)
def main(): def main():
if not HAS_F5SDK:
raise F5ModuleError("The python f5-sdk module is required")
spec = ArgumentSpec() spec = ArgumentSpec()
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=spec.supports_check_mode, supports_check_mode=spec.supports_check_mode
f5_product_name=spec.f5_product_name
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
try: try:
mm = ModuleManager(client) client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
cleanup_tokens(client) cleanup_tokens(client)
client.module.exit_json(**results) module.exit_json(**results)
except F5ModuleError as e: except F5ModuleError as ex:
cleanup_tokens(client) cleanup_tokens(client)
client.module.fail_json(msg=str(e)) module.fail_json(msg=str(ex))
if __name__ == '__main__': if __name__ == '__main__':

@ -72,13 +72,10 @@ options:
default: Common default: Common
version_added: 2.5 version_added: 2.5
notes: notes:
- Requires the f5-sdk Python package on the host. This is as easy as pip
install f5-sdk.
- Requires the netaddr Python package on the host. - Requires the netaddr Python package on the host.
extends_documentation_fragment: f5 extends_documentation_fragment: f5
requirements: requirements:
- netaddr - netaddr
- f5-sdk
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
''' '''
@ -218,16 +215,37 @@ vlan:
import re import re
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.f5_utils import AnsibleF5Parameters from ansible.module_utils.basic import env_fallback
from ansible.module_utils.f5_utils import HAS_F5SDK
from ansible.module_utils.f5_utils import F5ModuleError HAS_DEVEL_IMPORTS = False
from ansible.module_utils.six import iteritems
from collections import defaultdict
try: try:
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError # Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK
from library.module_utils.network.f5.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import fqdn_name
from library.module_utils.network.f5.common import f5_argument_spec
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
HAS_DEVEL_IMPORTS = True
except ImportError: except ImportError:
# Upstream Ansible
from ansible.module_utils.network.f5.bigip import HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client
from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters
from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
try: try:
@ -255,36 +273,6 @@ class Parameters(AnsibleF5Parameters):
'trafficGroup', 'allowService', 'vlan', 'address' 'trafficGroup', 'allowService', 'vlan', 'address'
] ]
def __init__(self, params=None):
self._values = defaultdict(lambda: None)
self._values['__warnings'] = []
if params:
self.update(params=params)
def update(self, params=None):
if params:
for k, v in iteritems(params):
if self.api_map is not None and k in self.api_map:
map_key = self.api_map[k]
else:
map_key = k
# Handle weird API parameters like `dns.proxy.__iter__` by
# using a map provided by the module developer
class_attr = getattr(type(self), map_key, None)
if isinstance(class_attr, property):
# There is a mapped value for the api_map key
if class_attr.fset is None:
# If the mapped value does not have
# an associated setter
self._values[map_key] = v
else:
# The mapped value has a setter
setattr(self, map_key, v)
else:
# If the mapped value is not a @property
self._values[map_key] = v
def to_return(self): def to_return(self):
result = {} result = {}
for returnable in self.returnables: for returnable in self.returnables:
@ -292,16 +280,6 @@ class Parameters(AnsibleF5Parameters):
result = self._filter_params(result) result = self._filter_params(result)
return result return result
def api_params(self):
result = {}
for api_attribute in self.api_attributes:
if self.api_map is not None and api_attribute in self.api_map:
result[api_attribute] = getattr(self, self.api_map[api_attribute])
else:
result[api_attribute] = getattr(self, api_attribute)
result = self._filter_params(result)
return result
@property @property
def address(self): def address(self):
address = "{0}%{1}/{2}".format( address = "{0}%{1}/{2}".format(
@ -424,7 +402,8 @@ class Parameters(AnsibleF5Parameters):
) )
else: else:
result.append(svc) result.append(svc)
return set(result) result = sorted(list(set(result)))
return result
def _fqdn_name(self, value): def _fqdn_name(self, value):
if value is not None and not value.startswith('/'): if value is not None and not value.startswith('/'):
@ -468,7 +447,9 @@ class ApiParameters(Parameters):
@property @property
def allow_service(self): def allow_service(self):
return self._values['allow_service'] if self._values['allow_service'] is None:
return None
return sorted(self._values['allow_service'])
@property @property
def trafficGroup(self): def trafficGroup(self):
@ -485,16 +466,17 @@ class ApiParameters(Parameters):
@allowService.setter @allowService.setter
def allowService(self, value): def allowService(self, value):
if value == 'all': if value == 'all':
self._values['allow_service'] = set(['all']) self._values['allow_service'] = ['all']
else: else:
self._values['allow_service'] = set([str(x) for x in value]) self._values['allow_service'] = sorted([str(x) for x in value])
class ModuleManager(object): class ModuleManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None)
self.have = None self.have = None
self.want = Parameters(self.client.module.params) self.want = Parameters(params=self.module.params)
self.changes = ApiParameters() self.changes = ApiParameters()
def _set_changed_options(self): def _set_changed_options(self):
@ -503,7 +485,7 @@ class ModuleManager(object):
if getattr(self.want, key) is not None: if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key) changed[key] = getattr(self.want, key)
if changed: if changed:
self.changes = Parameters(changed) self.changes = Parameters(params=changed)
def _update_changed_options(self): def _update_changed_options(self):
diff = Difference(self.want, self.have) diff = Difference(self.want, self.have)
@ -519,7 +501,7 @@ class ModuleManager(object):
else: else:
changed[k] = change changed[k] = change
if changed: if changed:
self.changes = ApiParameters(changed) self.changes = ApiParameters(params=changed)
return True return True
return False return False
@ -566,14 +548,14 @@ class ModuleManager(object):
partition=self.want.partition partition=self.want.partition
) )
result = resource.attrs result = resource.attrs
params = ApiParameters(result) params = ApiParameters(params=result)
return params return params
def update(self): def update(self):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
if not self.should_update(): if not self.should_update():
return False return False
if self.client.check_mode: if self.module.check_mode:
return True return True
self.update_on_device() self.update_on_device()
return True return True
@ -599,6 +581,13 @@ class ModuleManager(object):
self.want.update({'traffic_group': '/Common/traffic-group-local-only'}) self.want.update({'traffic_group': '/Common/traffic-group-local-only'})
if self.want.route_domain is None: if self.want.route_domain is None:
self.want.update({'route_domain': 0}) self.want.update({'route_domain': 0})
if self.want.allow_service:
if 'all' in self.want.allow_service:
self.want.update(dict(allow_service='all'))
elif 'none' in self.want.allow_service:
self.want.update(dict(allow_service=[]))
elif 'default' in self.want.allow_service:
self.want.update(dict(allow_service=['default']))
if self.want.check_mode: if self.want.check_mode:
return True return True
self.create_on_device() self.create_on_device()
@ -616,7 +605,7 @@ class ModuleManager(object):
) )
def remove(self): def remove(self):
if self.client.check_mode: if self.module.check_mode:
return True return True
self.remove_from_device() self.remove_from_device()
if self.exists(): if self.exists():
@ -674,23 +663,20 @@ class Difference(object):
This is a convenience function to massage the values the user has This is a convenience function to massage the values the user has
supplied so that they are formatted in such a way that BIG-IP will supplied so that they are formatted in such a way that BIG-IP will
accept them and apply the specified policy. accept them and apply the specified policy.
:param services: The services to format. This is always a Python set
:return:
""" """
if self.want.allow_service is None: if self.want.allow_service is None:
return None return None
result = list(self.want.allow_service) result = self.want.allow_service
if self.want.allow_service == self.have.allow_service: if result[0] == 'none' and self.have.allow_service is None:
return None
elif result[0] == 'none' and self.have.allow_service is None:
return None return None
elif result[0] == 'all': elif result[0] == 'all' and self.have.allow_service[0] != 'all':
return 'all' return 'all'
elif result[0] == 'none': elif result[0] == 'none':
return [] return []
else: elif self.have.allow_service is None:
return list(result) return result
elif set(self.want.allow_service) != set(self.have.allow_service):
return result
@property @property
def netmask(self): def netmask(self):
@ -744,39 +730,49 @@ class Difference(object):
class ArgumentSpec(object): class ArgumentSpec(object):
def __init__(self): def __init__(self):
self.supports_check_mode = True self.supports_check_mode = True
self.argument_spec = dict( argument_spec = dict(
address=dict(), address=dict(),
allow_service=dict(type='list'), allow_service=dict(type='list'),
name=dict(required=True), name=dict(required=True),
netmask=dict(), netmask=dict(),
traffic_group=dict(), traffic_group=dict(),
vlan=dict(), vlan=dict(),
route_domain=dict() route_domain=dict(),
state=dict(
default='present',
choices=['present', 'absent']
),
partition=dict(
default='Common',
fallback=(env_fallback, ['F5_PARTITION'])
) )
self.f5_product_name = 'bigip' )
self.argument_spec = {}
self.argument_spec.update(f5_argument_spec)
self.argument_spec.update(argument_spec)
def main(): def main():
if not HAS_F5SDK:
raise F5ModuleError("The python f5-sdk module is required")
if not HAS_NETADDR:
raise F5ModuleError("The python netaddr module is required")
spec = ArgumentSpec() spec = ArgumentSpec()
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=spec.supports_check_mode, supports_check_mode=spec.supports_check_mode
f5_product_name=spec.f5_product_name
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
if not HAS_NETADDR:
module.fail_json(msg="The python netaddr module is required")
try: try:
mm = ModuleManager(client) client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
client.module.exit_json(**results) cleanup_tokens(client)
except F5ModuleError as e: module.exit_json(**results)
client.module.fail_json(msg=str(e)) except F5ModuleError as ex:
cleanup_tokens(client)
module.fail_json(msg=str(ex))
if __name__ == '__main__': if __name__ == '__main__':

@ -42,13 +42,9 @@ options:
default: 'Common' default: 'Common'
version_added: 2.5 version_added: 2.5
notes: notes:
- Requires the f5-sdk Python package on the host. This is as easy as
pip install f5-sdk
- Requires the netaddr Python package on the host. This is as easy as - Requires the netaddr Python package on the host. This is as easy as
pip install netaddr pip install netaddr
extends_documentation_fragment: f5 extends_documentation_fragment: f5
requirements:
- f5-sdk
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
''' '''
@ -97,14 +93,37 @@ members:
import os import os
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.f5_utils import AnsibleF5Parameters from ansible.module_utils.basic import env_fallback
from ansible.module_utils.f5_utils import HAS_F5SDK
from ansible.module_utils.f5_utils import F5ModuleError HAS_DEVEL_IMPORTS = False
try: try:
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError # Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK
from library.module_utils.network.f5.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import fqdn_name
from library.module_utils.network.f5.common import f5_argument_spec
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
HAS_DEVEL_IMPORTS = True
except ImportError: except ImportError:
# Upstream Ansible
from ansible.module_utils.network.f5.bigip import HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client
from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters
from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
try: try:
@ -172,6 +191,10 @@ class Parameters(AnsibleF5Parameters):
) )
class Changes(Parameters):
pass
class Difference(object): class Difference(object):
def __init__(self, want, have=None): def __init__(self, want, have=None):
self.want = want self.want = want
@ -204,11 +227,12 @@ class Difference(object):
class ModuleManager(object): class ModuleManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None)
self.have = None self.have = None
self.want = Parameters(self.client.module.params) self.want = Parameters(params=self.module.params)
self.changes = Parameters() self.changes = Changes()
def _set_changed_options(self): def _set_changed_options(self):
changed = {} changed = {}
@ -216,7 +240,7 @@ class ModuleManager(object):
if getattr(self.want, key) is not None: if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key) changed[key] = getattr(self.want, key)
if changed: if changed:
self.changes = Parameters(changed) self.changes = Changes(params=changed)
def _update_changed_options(self): def _update_changed_options(self):
diff = Difference(self.want, self.have) diff = Difference(self.want, self.have)
@ -229,7 +253,7 @@ class ModuleManager(object):
else: else:
changed[k] = change changed[k] = change
if changed: if changed:
self.changes = Parameters(changed) self.changes = Changes(params=changed)
return True return True
return False return False
@ -259,7 +283,7 @@ class ModuleManager(object):
if self.have: if self.have:
warnings += self.have._values.get('__warnings', []) warnings += self.have._values.get('__warnings', [])
for warning in warnings: for warning in warnings:
self.client.module.deprecate( self.module.deprecate(
msg=warning['msg'], msg=warning['msg'],
version=warning['version'] version=warning['version']
) )
@ -282,7 +306,7 @@ class ModuleManager(object):
partition=self.want.partition partition=self.want.partition
) )
result = resource.attrs result = resource.attrs
return Parameters(result) return Parameters(params=result)
def exists(self): def exists(self):
result = self.client.api.tm.ltm.snatpools.snatpool.exists( result = self.client.api.tm.ltm.snatpools.snatpool.exists(
@ -301,7 +325,7 @@ class ModuleManager(object):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
if not self.should_update(): if not self.should_update():
return False return False
if self.client.check_mode: if self.module.check_mode:
return True return True
self.update_on_device() self.update_on_device()
return True return True
@ -317,7 +341,7 @@ class ModuleManager(object):
def create(self): def create(self):
self._set_changed_options() self._set_changed_options()
if self.client.check_mode: if self.module.check_mode:
return True return True
self.create_on_device() self.create_on_device()
if not self.exists(): if not self.exists():
@ -333,7 +357,7 @@ class ModuleManager(object):
) )
def remove(self): def remove(self):
if self.client.check_mode: if self.module.check_mode:
return True return True
self.remove_from_device() self.remove_from_device()
if self.exists(): if self.exists():
@ -351,7 +375,7 @@ class ModuleManager(object):
class ArgumentSpec(object): class ArgumentSpec(object):
def __init__(self): def __init__(self):
self.supports_check_mode = True self.supports_check_mode = True
self.argument_spec = dict( argument_spec = dict(
name=dict(required=True), name=dict(required=True),
members=dict( members=dict(
type='list', type='list',
@ -360,36 +384,43 @@ class ArgumentSpec(object):
state=dict( state=dict(
default='present', default='present',
choices=['absent', 'present'] choices=['absent', 'present']
),
partition=dict(
default='Common',
fallback=(env_fallback, ['F5_PARTITION'])
) )
) )
self.argument_spec = {}
self.argument_spec.update(f5_argument_spec)
self.argument_spec.update(argument_spec)
self.required_if = [ self.required_if = [
['state', 'present', ['members']] ['state', 'present', ['members']]
] ]
self.f5_product_name = 'bigip'
def main(): def main():
if not HAS_F5SDK:
raise F5ModuleError("The python f5-sdk module is required")
if not HAS_NETADDR:
raise F5ModuleError("The python netaddr module is required")
spec = ArgumentSpec() spec = ArgumentSpec()
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=spec.supports_check_mode, supports_check_mode=spec.supports_check_mode,
f5_product_name=spec.f5_product_name,
required_if=spec.required_if required_if=spec.required_if
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
if not HAS_NETADDR:
module.fail_json(msg="The python netaddr module is required")
try: try:
mm = ModuleManager(client) client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
client.module.exit_json(**results) cleanup_tokens(client)
except F5ModuleError as e: module.exit_json(**results)
client.module.fail_json(msg=str(e)) except F5ModuleError as ex:
cleanup_tokens(client)
module.fail_json(msg=str(ex))
if __name__ == '__main__': if __name__ == '__main__':
main() main()

@ -50,12 +50,7 @@ options:
location: location:
description: description:
- Specifies the description of this system's physical location. - Specifies the description of this system's physical location.
notes:
- Requires the f5-sdk Python package on the host. This is as easy as pip
install f5-sdk.
extends_documentation_fragment: f5 extends_documentation_fragment: f5
requirements:
- f5-sdk >= 2.2.0
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
''' '''
@ -108,14 +103,36 @@ location:
sample: US West 1a sample: US West 1a
''' '''
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.f5_utils import AnsibleF5Parameters
from ansible.module_utils.f5_utils import HAS_F5SDK HAS_DEVEL_IMPORTS = False
from ansible.module_utils.f5_utils import F5ModuleError
try: try:
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError # Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK
from library.module_utils.network.f5.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import fqdn_name
from library.module_utils.network.f5.common import f5_argument_spec
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
HAS_DEVEL_IMPORTS = True
except ImportError: except ImportError:
# Upstream Ansible
from ansible.module_utils.network.f5.bigip import HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client
from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters
from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
@ -149,23 +166,18 @@ class Parameters(AnsibleF5Parameters):
result = self._filter_params(result) result = self._filter_params(result)
return result return result
def api_params(self):
result = {} class Changes(Parameters):
for api_attribute in self.api_attributes: pass
if self.api_map is not None and api_attribute in self.api_map:
result[api_attribute] = getattr(self, self.api_map[api_attribute])
else:
result[api_attribute] = getattr(self, api_attribute)
result = self._filter_params(result)
return result
class ModuleManager(object): class ModuleManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None)
self.have = None self.have = None
self.want = Parameters(self.client.module.params) self.want = Parameters(params=self.module.params)
self.changes = Parameters() self.changes = Changes()
def _update_changed_options(self): def _update_changed_options(self):
changed = {} changed = {}
@ -176,7 +188,7 @@ class ModuleManager(object):
if attr1 != attr2: if attr1 != attr2:
changed[key] = attr1 changed[key] = attr1
if changed: if changed:
self.changes = Parameters(changed) self.changes = Changes(params=changed)
return True return True
return False return False
@ -203,7 +215,7 @@ class ModuleManager(object):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
if not self.should_update(): if not self.should_update():
return False return False
if self.client.check_mode: if self.module.check_mode:
return True return True
self.update_on_device() self.update_on_device()
return True return True
@ -216,14 +228,14 @@ class ModuleManager(object):
def read_current_from_device(self): def read_current_from_device(self):
resource = self.client.api.tm.sys.snmp.load() resource = self.client.api.tm.sys.snmp.load()
result = resource.attrs result = resource.attrs
return Parameters(result) return Parameters(params=result)
class ArgumentSpec(object): class ArgumentSpec(object):
def __init__(self): def __init__(self):
self.supports_check_mode = True self.supports_check_mode = True
self.choices = ['enabled', 'disabled'] self.choices = ['enabled', 'disabled']
self.argument_spec = dict( argument_spec = dict(
contact=dict(), contact=dict(),
agent_status_traps=dict( agent_status_traps=dict(
choices=self.choices choices=self.choices
@ -236,27 +248,30 @@ class ArgumentSpec(object):
), ),
location=dict() location=dict()
) )
self.f5_product_name = 'bigip' self.argument_spec = {}
self.argument_spec.update(f5_argument_spec)
self.argument_spec.update(argument_spec)
def main(): def main():
if not HAS_F5SDK:
raise F5ModuleError("The python f5-sdk module is required")
spec = ArgumentSpec() spec = ArgumentSpec()
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=spec.supports_check_mode, supports_check_mode=spec.supports_check_mode
f5_product_name=spec.f5_product_name
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
try: try:
mm = ModuleManager(client) client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
client.module.exit_json(**results) cleanup_tokens(client)
except F5ModuleError as e: module.exit_json(**results)
client.module.fail_json(msg=str(e)) except F5ModuleError as ex:
cleanup_tokens(client)
module.fail_json(msg=str(ex))
if __name__ == '__main__': if __name__ == '__main__':

@ -57,16 +57,17 @@ options:
choices: choices:
- present - present
- absent - absent
partition:
description:
- Device partition to manage resources on.
default: Common
version_added: 2.5
notes: notes:
- Requires the f5-sdk Python package on the host. This is as easy as pip
install f5-sdk.
- This module only supports version v1 and v2c of SNMP. - This module only supports version v1 and v2c of SNMP.
- The C(network) option is not supported on versions of BIG-IP < 12.1.0 because - The C(network) option is not supported on versions of BIG-IP < 12.1.0 because
the platform did not support that option until 12.1.0. If used on versions the platform did not support that option until 12.1.0. If used on versions
< 12.1.0, it will simply be ignored. < 12.1.0, it will simply be ignored.
extends_documentation_fragment: f5 extends_documentation_fragment: f5
requirements:
- f5-sdk >= 2.2.0
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
''' '''
@ -127,16 +128,38 @@ network:
sample: management sample: management
''' '''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import env_fallback
from ansible.module_utils.f5_utils import AnsibleF5Parameters
from ansible.module_utils.f5_utils import HAS_F5SDK
from ansible.module_utils.f5_utils import F5ModuleError
from distutils.version import LooseVersion from distutils.version import LooseVersion
HAS_DEVEL_IMPORTS = False
try: try:
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError # Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK
from library.module_utils.network.f5.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import fqdn_name
from library.module_utils.network.f5.common import f5_argument_spec
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
HAS_DEVEL_IMPORTS = True
except ImportError: except ImportError:
# Upstream Ansible
from ansible.module_utils.network.f5.bigip import HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client
from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters
from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
@ -166,16 +189,6 @@ class Parameters(AnsibleF5Parameters):
result = self._filter_params(result) result = self._filter_params(result)
return result return result
def api_params(self):
result = {}
for api_attribute in self.api_attributes:
if self.api_map is not None and api_attribute in self.api_map:
result[api_attribute] = getattr(self, self.api_map[api_attribute])
else:
result[api_attribute] = getattr(self, api_attribute)
result = self._filter_params(result)
return result
class NetworkedParameters(Parameters): class NetworkedParameters(Parameters):
updatables = [ updatables = [
@ -222,14 +235,16 @@ class NonNetworkedParameters(Parameters):
class ModuleManager(object): class ModuleManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None)
self.kwargs = kwargs
def exec_module(self): def exec_module(self):
if self.is_version_non_networked(): if self.is_version_non_networked():
manager = NonNetworkedManager(self.client) manager = NonNetworkedManager(**self.kwargs)
else: else:
manager = NetworkedManager(self.client) manager = NetworkedManager(**self.kwargs)
return manager.exec_module() return manager.exec_module()
@ -249,8 +264,9 @@ class ModuleManager(object):
class BaseManager(object): class BaseManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None)
self.have = None self.have = None
def exec_module(self): def exec_module(self):
@ -286,7 +302,7 @@ class BaseManager(object):
def create(self): def create(self):
self._set_changed_options() self._set_changed_options()
if self.client.check_mode: if self.module.check_mode:
return True return True
if all(getattr(self.want, v) is None for v in self.required_resources): if all(getattr(self.want, v) is None for v in self.required_resources):
raise F5ModuleError( raise F5ModuleError(
@ -306,7 +322,7 @@ class BaseManager(object):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
if not self.should_update(): if not self.should_update():
return False return False
if self.client.check_mode: if self.module.check_mode:
return True return True
self.update_on_device() self.update_on_device()
return True return True
@ -333,7 +349,7 @@ class BaseManager(object):
return False return False
def remove(self): def remove(self):
if self.client.check_mode: if self.module.check_mode:
return True return True
self.remove_from_device() self.remove_from_device()
if self.exists(): if self.exists():
@ -350,12 +366,12 @@ class BaseManager(object):
class NetworkedManager(BaseManager): class NetworkedManager(BaseManager):
def __init__(self, client): def __init__(self, *args, **kwargs):
super(NetworkedManager, self).__init__(client) super(NetworkedManager, self).__init__(**kwargs)
self.required_resources = [ self.required_resources = [
'version', 'community', 'destination', 'port', 'network' 'version', 'community', 'destination', 'port', 'network'
] ]
self.want = NetworkedParameters(self.client.module.params) self.want = NetworkedParameters(params=self.module.params)
self.changes = NetworkedParameters() self.changes = NetworkedParameters()
def _set_changed_options(self): def _set_changed_options(self):
@ -364,7 +380,7 @@ class NetworkedManager(BaseManager):
if getattr(self.want, key) is not None: if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key) changed[key] = getattr(self.want, key)
if changed: if changed:
self.changes = NetworkedParameters(changed) self.changes = NetworkedParameters(params=changed)
def _update_changed_options(self): def _update_changed_options(self):
changed = {} changed = {}
@ -375,7 +391,7 @@ class NetworkedManager(BaseManager):
if attr1 != attr2: if attr1 != attr2:
changed[key] = attr1 changed[key] = attr1
if changed: if changed:
self.changes = NetworkedParameters(changed) self.changes = NetworkedParameters(params=changed)
return True return True
return False return False
@ -386,7 +402,7 @@ class NetworkedManager(BaseManager):
) )
result = resource.attrs result = resource.attrs
self._ensure_network(result) self._ensure_network(result)
return NetworkedParameters(result) return NetworkedParameters(params=result)
def _ensure_network(self, result): def _ensure_network(self, result):
# BIG-IP's value for "default" is that the key does not # BIG-IP's value for "default" is that the key does not
@ -400,12 +416,12 @@ class NetworkedManager(BaseManager):
class NonNetworkedManager(BaseManager): class NonNetworkedManager(BaseManager):
def __init__(self, client): def __init__(self, *args, **kwargs):
super(NonNetworkedManager, self).__init__(client) super(NonNetworkedManager, self).__init__(**kwargs)
self.required_resources = [ self.required_resources = [
'version', 'community', 'destination', 'port' 'version', 'community', 'destination', 'port'
] ]
self.want = NonNetworkedParameters(self.client.module.params) self.want = NonNetworkedParameters(params=self.module.params)
self.changes = NonNetworkedParameters() self.changes = NonNetworkedParameters()
def _set_changed_options(self): def _set_changed_options(self):
@ -414,7 +430,7 @@ class NonNetworkedManager(BaseManager):
if getattr(self.want, key) is not None: if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key) changed[key] = getattr(self.want, key)
if changed: if changed:
self.changes = NonNetworkedParameters(changed) self.changes = NonNetworkedParameters(params=changed)
def _update_changed_options(self): def _update_changed_options(self):
changed = {} changed = {}
@ -425,7 +441,7 @@ class NonNetworkedManager(BaseManager):
if attr1 != attr2: if attr1 != attr2:
changed[key] = attr1 changed[key] = attr1
if changed: if changed:
self.changes = NonNetworkedParameters(changed) self.changes = NonNetworkedParameters(params=changed)
return True return True
return False return False
@ -435,13 +451,13 @@ class NonNetworkedManager(BaseManager):
partition=self.want.partition partition=self.want.partition
) )
result = resource.attrs result = resource.attrs
return NonNetworkedParameters(result) return NonNetworkedParameters(params=result)
class ArgumentSpec(object): class ArgumentSpec(object):
def __init__(self): def __init__(self):
self.supports_check_mode = True self.supports_check_mode = True
self.argument_spec = dict( argument_spec = dict(
name=dict( name=dict(
required=True required=True
), ),
@ -457,26 +473,36 @@ class ArgumentSpec(object):
state=dict( state=dict(
default='present', default='present',
choices=['absent', 'present'] choices=['absent', 'present']
),
partition=dict(
default='Common',
fallback=(env_fallback, ['F5_PARTITION'])
) )
) )
self.f5_product_name = 'bigip' self.argument_spec = {}
self.argument_spec.update(f5_argument_spec)
self.argument_spec.update(argument_spec)
def main(): def main():
if not HAS_F5SDK:
raise F5ModuleError("The python f5-sdk module is required")
spec = ArgumentSpec() spec = ArgumentSpec()
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=spec.supports_check_mode, supports_check_mode=spec.supports_check_mode
f5_product_name=spec.f5_product_name
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
mm = ModuleManager(client) try:
client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
client.module.exit_json(**results) cleanup_tokens(client)
module.exit_json(**results)
except F5ModuleError as ex:
cleanup_tokens(client)
module.fail_json(msg=str(ex))
if __name__ == '__main__': if __name__ == '__main__':

@ -37,12 +37,7 @@ options:
- daily - daily
- monthly - monthly
- weekly - weekly
notes:
- Requires the f5-sdk Python package on the host This is as easy as
C(pip install f5-sdk)
extends_documentation_fragment: f5 extends_documentation_fragment: f5
requirements:
- f5-sdk >= 3.0.6
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
''' '''
@ -86,16 +81,36 @@ frequency:
sample: weekly sample: weekly
''' '''
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.f5_utils import AnsibleF5Parameters
from ansible.module_utils.f5_utils import HAS_F5SDK HAS_DEVEL_IMPORTS = False
from ansible.module_utils.f5_utils import F5ModuleError
from ansible.module_utils.six import iteritems
from collections import defaultdict
try: try:
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError # Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK
from library.module_utils.network.f5.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import fqdn_name
from library.module_utils.network.f5.common import f5_argument_spec
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
HAS_DEVEL_IMPORTS = True
except ImportError: except ImportError:
# Upstream Ansible
from ansible.module_utils.network.f5.bigip import HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client
from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters
from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
@ -117,46 +132,6 @@ class Parameters(AnsibleF5Parameters):
'auto_check', 'auto_phone_home', 'frequency' 'auto_check', 'auto_phone_home', 'frequency'
] ]
def __init__(self, params=None):
self._values = defaultdict(lambda: None)
self._values['__warnings'] = []
if params:
self.update(params=params)
def update(self, params=None):
if params:
for k, v in iteritems(params):
if self.api_map is not None and k in self.api_map:
map_key = self.api_map[k]
else:
map_key = k
# Handle weird API parameters like `dns.proxy.__iter__` by
# using a map provided by the module developer
class_attr = getattr(type(self), map_key, None)
if isinstance(class_attr, property):
# There is a mapped value for the api_map key
if class_attr.fset is None:
# If the mapped value does not have
# an associated setter
self._values[map_key] = v
else:
# The mapped value has a setter
setattr(self, map_key, v)
else:
# If the mapped value is not a @property
self._values[map_key] = v
def api_params(self):
result = {}
for api_attribute in self.api_attributes:
if self.api_map is not None and api_attribute in self.api_map:
result[api_attribute] = getattr(self, self.api_map[api_attribute])
else:
result[api_attribute] = getattr(self, api_attribute)
result = self._filter_params(result)
return result
class ApiParameters(Parameters): class ApiParameters(Parameters):
@property @property
@ -241,10 +216,11 @@ class Difference(object):
class ModuleManager(object): class ModuleManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None)
self.have = None self.have = None
self.want = ModuleParameters(self.client.module.params) self.want = ModuleParameters(params=self.module.params)
self.changes = UsableChanges() self.changes = UsableChanges()
def exec_module(self): def exec_module(self):
@ -255,7 +231,7 @@ class ModuleManager(object):
except iControlUnexpectedHTTPError as e: except iControlUnexpectedHTTPError as e:
raise F5ModuleError(str(e)) raise F5ModuleError(str(e))
reportable = ReportableChanges(self.changes.to_return()) reportable = ReportableChanges(params=self.changes.to_return())
changes = reportable.to_return() changes = reportable.to_return()
result.update(**changes) result.update(**changes)
result.update(dict(changed=changed)) result.update(dict(changed=changed))
@ -265,7 +241,7 @@ class ModuleManager(object):
def _announce_deprecations(self, result): def _announce_deprecations(self, result):
warnings = result.pop('__warnings', []) warnings = result.pop('__warnings', [])
for warning in warnings: for warning in warnings:
self.client.module.deprecate( self.module.deprecate(
msg=warning['msg'], msg=warning['msg'],
version=warning['version'] version=warning['version']
) )
@ -284,7 +260,7 @@ class ModuleManager(object):
else: else:
changed[k] = change changed[k] = change
if changed: if changed:
self.changes = UsableChanges(changed) self.changes = UsableChanges(params=changed)
return True return True
return False return False
@ -298,7 +274,7 @@ class ModuleManager(object):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
if not self.should_update(): if not self.should_update():
return False return False
if self.client.check_mode: if self.module.check_mode:
return True return True
self.update_on_device() self.update_on_device()
return True return True
@ -311,13 +287,13 @@ class ModuleManager(object):
def read_current_from_device(self): def read_current_from_device(self):
resource = self.client.api.tm.sys.software.update.load() resource = self.client.api.tm.sys.software.update.load()
result = resource.attrs result = resource.attrs
return ApiParameters(result) return ApiParameters(params=result)
class ArgumentSpec(object): class ArgumentSpec(object):
def __init__(self): def __init__(self):
self.supports_check_mode = True self.supports_check_mode = True
self.argument_spec = dict( argument_spec = dict(
auto_check=dict( auto_check=dict(
type='bool' type='bool'
), ),
@ -328,39 +304,30 @@ class ArgumentSpec(object):
choices=['daily', 'monthly', 'weekly'] choices=['daily', 'monthly', 'weekly']
) )
) )
self.f5_product_name = 'bigip' self.argument_spec = {}
self.argument_spec.update(f5_argument_spec)
self.argument_spec.update(argument_spec)
def cleanup_tokens(client):
try:
resource = client.api.shared.authz.tokens_s.token.load(
name=client.api.icrs.token
)
resource.delete()
except Exception:
pass
def main(): def main():
if not HAS_F5SDK:
raise F5ModuleError("The python f5-sdk module is required")
spec = ArgumentSpec() spec = ArgumentSpec()
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=spec.supports_check_mode, supports_check_mode=spec.supports_check_mode
f5_product_name=spec.f5_product_name
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
try: try:
mm = ModuleManager(client) client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
cleanup_tokens(client) cleanup_tokens(client)
client.module.exit_json(**results) module.exit_json(**results)
except F5ModuleError as e: except F5ModuleError as ex:
cleanup_tokens(client) cleanup_tokens(client)
client.module.fail_json(msg=str(e)) module.fail_json(msg=str(ex))
if __name__ == '__main__': if __name__ == '__main__':

@ -21,23 +21,15 @@ description:
disk, in PEM format. disk, in PEM format.
version_added: 2.2 version_added: 2.2
options: options:
cert_content: content:
description: description:
- When used instead of 'cert_src', sets the contents of a certificate directly - Sets the contents of a certificate directly to the specified value.
to the specified value. This is used with lookup plugins or for anything This is used with lookup plugins or for anything with formatting or
with formatting or templating. Either one of C(key_src), - C(content) must be provided when C(state) is C(present).
C(key_content), C(cert_src) or C(cert_content) must be provided when aliases: ['cert_content']
C(state) is C(present).
key_content:
description:
- When used instead of 'key_src', sets the contents of a certificate key
directly to the specified value. This is used with lookup plugins or for
anything with formatting or templating. Either one of C(key_src),
C(key_content), C(cert_src) or C(cert_content) must be provided when
C(state) is C(present).
state: state:
description: description:
- Certificate and key state. This determines if the provided certificate - Certificate state. This determines if the provided certificate
and key is to be made C(present) on the device or C(absent). and key is to be made C(present) on the device or C(absent).
default: present default: present
choices: choices:
@ -45,27 +37,20 @@ options:
- absent - absent
name: name:
description: description:
- SSL Certificate Name. This is the cert/key pair name used - SSL Certificate Name. This is the cert name used when importing a certificate
when importing a certificate/key into the F5. It also into the F5. It also determines the filenames of the objects on the LTM.
determines the filenames of the objects on the LTM
(:Partition:name.cer_11111_1 and :Partition_name.key_11111_1).
required: True required: True
cert_src: issuer_cert:
description:
- This is the local filename of the certificate. Either one of C(key_src),
C(key_content), C(cert_src) or C(cert_content) must be provided when
C(state) is C(present).
key_src:
description: description:
- This is the local filename of the private key. Either one of C(key_src), - Issuer certificate used for OCSP monitoring.
C(key_content), C(cert_src) or C(cert_content) must be provided when - This parameter is only valid on versions of BIG-IP 13.0.0 or above.
C(state) is C(present). version_added: 2.5
passphrase: partition:
description: description:
- Passphrase on certificate private key - Device partition to manage resources on.
default: Common
version_added: 2.5
notes: notes:
- Requires the f5-sdk Python package on the host. This is as easy as pip
install f5-sdk.
- This module does not behave like other modules that you might include in - This module does not behave like other modules that you might include in
roles where referencing files or templates first looks in the role's roles where referencing files or templates first looks in the role's
files or templates directory. To have it behave that way, use the Ansible files or templates directory. To have it behave that way, use the Ansible
@ -73,24 +58,13 @@ notes:
a role context. a role context.
extends_documentation_fragment: f5 extends_documentation_fragment: f5
requirements: requirements:
- f5-sdk >= 3.0.3
- BIG-IP >= v12 - BIG-IP >= v12
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
''' '''
EXAMPLES = r'''
- name: Import PEM Certificate from local disk
bigip_ssl_certificate:
name: certificate-name
server: lb.mydomain.com
user: admin
password: secret
state: present
cert_src: /path/to/cert.crt
key_src: /path/to/key.key
delegate_to: localhost
EXAMPLES = r'''
- name: Use a file lookup to import PEM Certificate - name: Use a file lookup to import PEM Certificate
bigip_ssl_certificate: bigip_ssl_certificate:
name: certificate-name name: certificate-name
@ -98,8 +72,7 @@ EXAMPLES = r'''
user: admin user: admin
password: secret password: secret
state: present state: present
cert_content: "{{ lookup('file', '/path/to/cert.crt') }}" content: "{{ lookup('file', '/path/to/cert.crt') }}"
key_content: "{{ lookup('file', '/path/to/key.key') }}"
delegate_to: localhost delegate_to: localhost
- name: Use a file lookup to import CA certificate chain - name: Use a file lookup to import CA certificate chain
@ -109,10 +82,10 @@ EXAMPLES = r'''
user: admin user: admin
password: secret password: secret
state: present state: present
cert_content: "{{ lookup('file', '/path/to/ca-chain.crt') }}" content: "{{ lookup('file', '/path/to/ca-chain.crt') }}"
delegate_to: localhost delegate_to: localhost
- name: "Delete Certificate" - name: Delete Certificate
bigip_ssl_certificate: bigip_ssl_certificate:
name: certificate-name name: certificate-name
server: lb.mydomain.com server: lb.mydomain.com
@ -128,38 +101,18 @@ cert_name:
returned: created returned: created
type: string type: string
sample: cert1 sample: cert1
key_filename: filename:
description:
- The name of the SSL certificate key. The C(key_filename) and
C(cert_filename) will be similar to each other, however the
C(key_filename) will have a C(.key) extension.
returned: created
type: string
sample: cert1.key
key_checksum:
description: SHA1 checksum of the key that was provided.
returned: changed and created
type: string
sample: cf23df2207d99a74fbe169e3eba035e633b65d94
key_source_path:
description: Path on BIG-IP where the source of the key is stored
returned: created
type: string
sample: /var/config/rest/downloads/cert1.key
cert_filename:
description: description:
- The name of the SSL certificate. The C(cert_filename) and - The name of the SSL certificate.
C(key_filename) will be similar to each other, however the
C(cert_filename) will have a C(.crt) extension.
returned: created returned: created
type: string type: string
sample: cert1.crt sample: cert1.crt
cert_checksum: checksum:
description: SHA1 checksum of the cert that was provided. description: SHA1 checksum of the cert that was provided.
returned: changed and created returned: changed and created
type: string type: string
sample: f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0 sample: f7ff9e8b7bb2e09b70935a5d785e0cc5d9d0abf0
cert_source_path: source_path:
description: Path on BIG-IP where the source of the certificate is stored. description: Path on BIG-IP where the source of the certificate is stored.
returned: created returned: created
type: string type: string
@ -171,47 +124,58 @@ import hashlib
import os import os
import re import re
try: from ansible.module_utils.basic import AnsibleModule
from StringIO import StringIO from ansible.module_utils.basic import env_fallback
except ImportError:
from io import StringIO
from ansible.module_utils.f5_utils import AnsibleF5Client HAS_DEVEL_IMPORTS = False
from ansible.module_utils.f5_utils import AnsibleF5Parameters
from ansible.module_utils.f5_utils import HAS_F5SDK
from ansible.module_utils.f5_utils import F5ModuleError
from ansible.module_utils.f5_utils import iteritems
try: try:
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError # Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK
from library.module_utils.network.f5.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import fqdn_name
from library.module_utils.network.f5.common import f5_argument_spec
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
HAS_DEVEL_IMPORTS = True
except ImportError: except ImportError:
# Upstream Ansible
from ansible.module_utils.network.f5.bigip import HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client
from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters
from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
def __init__(self, params=None): api_map = {
super(Parameters, self).__init__(params) 'sourcePath': 'source_path',
self._values['__warnings'] = [] 'issuerCert': 'issuer_cert'
}
def to_return(self): updatables = ['content', 'issuer_cert']
result = {}
try:
for returnable in self.returnables:
result[returnable] = getattr(self, returnable)
result = self._filter_params(result)
except Exception:
pass
return result
def api_params(self): returnables = [
result = {} 'filename', 'checksum', 'source_path', 'issuer_cert'
for api_attribute in self.api_attributes: ]
if self.api_map is not None and api_attribute in self.api_map:
result[api_attribute] = getattr(self, self.api_map[api_attribute]) api_attributes = ['issuerCert']
else:
result[api_attribute] = getattr(self, api_attribute)
result = self._filter_params(result)
return result
def _get_hash(self, content): def _get_hash(self, content):
k = hashlib.sha1() k = hashlib.sha1()
@ -223,6 +187,13 @@ class Parameters(AnsibleF5Parameters):
k.update(data.encode('utf-8')) k.update(data.encode('utf-8'))
return k.hexdigest() return k.hexdigest()
def _fqdn_name(self, value):
if value is not None and not value.startswith('/'):
return '/{0}/{1}'.format(self.partition, value)
return value
class ApiParameters(Parameters):
@property @property
def checksum(self): def checksum(self):
if self._values['checksum'] is None: if self._values['checksum'] is None:
@ -234,148 +205,105 @@ class Parameters(AnsibleF5Parameters):
else: else:
return None return None
@property
def filename(self):
return self._values['name']
class KeyParameters(Parameters):
api_map = {
'sourcePath': 'key_source_path'
}
updatables = ['key_source_path']
returnables = ['key_filename', 'key_checksum', 'key_source_path']
api_attributes = ['passphrase', 'sourcePath']
class ModuleParameters(Parameters):
@property @property
def key_filename(self): def issuer_cert(self):
if self.name.endswith('.key'): if self._values['issuer_cert'] is None:
return self.name return None
name = self._fqdn_name(self._values['issuer_cert'])
if name.endswith('.crt'):
return name
else: else:
return self.name + '.key' return name + '.crt'
@property @property
def key_checksum(self): def checksum(self):
if self.key_content is None: if self.content is None:
return None return None
return self._get_hash(self.key_content) return self._get_hash(self.content)
@property @property
def key_src(self): def filename(self):
if self._values['key_src'] is None: if self.name.endswith('.crt'):
return None return self.name
else:
self._values['__warnings'].append( return self.name + '.crt'
dict(
msg="The key_src param is deprecated",
version='2.4'
)
)
try:
with open(self._values['key_src']) as fh:
self.key_content = fh.read()
except IOError:
raise F5ModuleError(
"The specified 'key_src' does not exist"
)
@property @property
def key_source_path(self): def source_path(self):
result = 'file://' + os.path.join( result = 'file://' + os.path.join(
BaseManager.download_path, ModuleManager.download_path,
self.key_filename self.filename
) )
return result return result
class CertParameters(Parameters): class Changes(Parameters):
api_map = { def to_return(self):
'sourcePath': 'cert_source_path' result = {}
} try:
for returnable in self.returnables:
result[returnable] = getattr(self, returnable)
result = self._filter_params(result)
except Exception:
pass
return result
updatables = ['cert_source_path']
returnables = ['cert_filename', 'cert_checksum', 'cert_source_path'] class ReportableChanges(Changes):
pass
api_attributes = ['sourcePath']
@property class UsableChanges(Changes):
def cert_checksum(self): pass
if self.cert_content is None:
return None
return self._get_hash(self.cert_content)
@property
def cert_filename(self):
if self.name.endswith('.crt'):
return self.name
else:
return self.name + '.crt'
@property class Difference(object):
def cert_src(self): def __init__(self, want, have=None):
if self._values['cert_src'] is None: self.want = want
return None self.have = have
self._values['__warnings'].append( def compare(self, param):
dict( try:
msg="The cert_src param is deprecated", result = getattr(self, param)
version='2.4' return result
) except AttributeError:
) result = self.__default(param)
return result
def __default(self, param):
attr1 = getattr(self.want, param)
try: try:
with open(self._value['cert_src']) as fh: attr2 = getattr(self.have, param)
self.cert_content = fh.read() if attr1 != attr2:
except IOError: return attr1
raise F5ModuleError( except AttributeError:
"The specified 'cert_src' does not exist" return attr1
)
@property @property
def cert_source_path(self): def content(self):
result = 'file://' + os.path.join( if self.want.checksum != self.have.checksum:
BaseManager.download_path, result = dict(
self.cert_filename checksum=self.want.checksum,
content=self.want.content
) )
return result return result
class ModuleManager(object): class ModuleManager(object):
def __init__(self, client):
self.client = client
def exec_module(self):
manager1 = self.get_manager('certificate')
manager2 = self.get_manager('key')
result = self.execute_managers([manager1, manager2])
return result
def execute_managers(self, managers):
results = dict(changed=False)
for manager in managers:
result = manager.exec_module()
for k, v in iteritems(result):
if k == 'changed':
if v is True:
results['changed'] = True
else:
results[k] = v
return results
def get_manager(self, type):
if type == 'certificate':
return CertificateManager(self.client)
elif type == 'key':
return KeyManager(self.client)
class BaseManager(object):
download_path = '/var/config/rest/downloads' download_path = '/var/config/rest/downloads'
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.have = None self.client = kwargs.get('client', None)
self.want = ModuleParameters(params=self.module.params)
self.have = ApiParameters()
self.changes = UsableChanges()
def exec_module(self): def exec_module(self):
changed = False changed = False
@ -390,20 +318,17 @@ class BaseManager(object):
except iControlUnexpectedHTTPError as e: except iControlUnexpectedHTTPError as e:
raise F5ModuleError(str(e)) raise F5ModuleError(str(e))
changes = self.changes.to_return() reportable = ReportableChanges(params=self.changes.to_return())
changes = reportable.to_return()
result.update(**changes) result.update(**changes)
result.update(dict(changed=changed)) result.update(dict(changed=changed))
self._announce_deprecations() self._announce_deprecations(result)
return result return result
def _announce_deprecations(self): def _announce_deprecations(self, result):
warnings = [] warnings = result.pop('__warnings', [])
if self.want:
warnings += self.want._values.get('__warnings', [])
if self.have:
warnings += self.have._values.get('__warnings', [])
for warning in warnings: for warning in warnings:
self.client.module.deprecate( self.module.deprecate(
msg=warning['msg'], msg=warning['msg'],
version=warning['version'] version=warning['version']
) )
@ -416,7 +341,7 @@ class BaseManager(object):
def create(self): def create(self):
self._set_changed_options() self._set_changed_options()
if self.client.check_mode: if self.module.check_mode:
return True return True
self.create_on_device() self.create_on_device()
return True return True
@ -431,7 +356,7 @@ class BaseManager(object):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
if not self.should_update(): if not self.should_update():
return False return False
if self.client.check_mode: if self.module.check_mode:
return True return True
self.update_on_device() self.update_on_device()
return True return True
@ -442,257 +367,138 @@ class BaseManager(object):
return False return False
def remove(self): def remove(self):
if self.client.check_mode: if self.module.check_mode:
return True return True
self.remove_from_device() self.remove_from_device()
return True return True
class CertificateManager(BaseManager):
def __init__(self, client):
super(CertificateManager, self).__init__(client)
self.want = CertParameters(self.client.module.params)
self.changes = CertParameters()
def _set_changed_options(self): def _set_changed_options(self):
changed = {} changed = {}
try: for key in Parameters.returnables:
for key in CertParameters.returnables:
if getattr(self.want, key) is not None: if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key) changed[key] = getattr(self.want, key)
if changed: if changed:
self.changes = CertParameters(changed) self.changes = UsableChanges(params=changed)
except Exception:
pass
def _update_changed_options(self): def _update_changed_options(self):
changed = {} diff = Difference(self.want, self.have)
try: updatables = Parameters.updatables
for key in CertParameters.updatables: changed = dict()
if getattr(self.want, key) is not None: for k in updatables:
attr1 = getattr(self.want, key) change = diff.compare(k)
attr2 = getattr(self.have, key) if change is None:
if attr1 != attr2: continue
changed[key] = attr1 else:
if self.want.cert_checksum != self.have.checksum: if isinstance(change, dict):
changed['cert_checksum'] = self.want.cert_checksum changed.update(change)
else:
changed[k] = change
if changed: if changed:
self.changes = CertParameters(changed) self.changes = UsableChanges(params=changed)
return True return True
except Exception:
pass
return False return False
def exists(self): def exists(self):
result = self.client.api.tm.sys.file.ssl_certs.ssl_cert.exists( result = self.client.api.tm.sys.file.ssl_certs.ssl_cert.exists(
name=self.want.cert_filename, name=self.want.filename,
partition=self.want.partition partition=self.want.partition
) )
return result return result
def present(self):
if self.want.cert_content is None:
return False
return super(CertificateManager, self).present()
def should_update(self):
result = self._update_changed_options()
if result:
return True
return False
def update_on_device(self): def update_on_device(self):
content = StringIO(self.want.cert_content) params = self.changes.api_params()
content = StringIO(self.want.content)
self.client.api.shared.file_transfer.uploads.upload_stringio( self.client.api.shared.file_transfer.uploads.upload_stringio(
content, self.want.cert_filename content, self.want.filename
) )
resource = self.client.api.tm.sys.file.ssl_certs.ssl_cert.load( resource = self.client.api.tm.sys.file.ssl_certs.ssl_cert.load(
name=self.want.cert_filename, name=self.want.filename,
partition=self.want.partition partition=self.want.partition
) )
resource.update() resource.update(**params)
def create_on_device(self): def create_on_device(self):
content = StringIO(self.want.cert_content) content = StringIO(self.want.content)
self.client.api.shared.file_transfer.uploads.upload_stringio(
content, self.want.cert_filename
)
self.client.api.tm.sys.file.ssl_certs.ssl_cert.create(
sourcePath=self.want.cert_source_path,
name=self.want.cert_filename,
partition=self.want.partition
)
def read_current_from_device(self):
resource = self.client.api.tm.sys.file.ssl_certs.ssl_cert.load(
name=self.want.cert_filename,
partition=self.want.partition
)
result = resource.attrs
return CertParameters(result)
def remove_from_device(self):
resource = self.client.api.tm.sys.file.ssl_certs.ssl_cert.load(
name=self.want.cert_filename,
partition=self.want.partition
)
resource.delete()
def remove(self):
result = super(CertificateManager, self).remove()
if self.exists() and not self.client.check_mode:
raise F5ModuleError("Failed to delete the certificate")
return result
class KeyManager(BaseManager):
def __init__(self, client):
super(KeyManager, self).__init__(client)
self.want = KeyParameters(self.client.module.params)
self.changes = KeyParameters()
def _set_changed_options(self):
changed = {}
try:
for key in KeyParameters.returnables:
if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key)
if changed:
self.changes = KeyParameters(changed)
except Exception:
pass
def _update_changed_options(self):
changed = {}
try:
for key in KeyParameters.updatables:
if getattr(self.want, key) is not None:
attr1 = getattr(self.want, key)
attr2 = getattr(self.have, key)
if attr1 != attr2:
changed[key] = attr1
if self.want.key_checksum != self.have.checksum:
changed['key_checksum'] = self.want.key_checksum
if changed:
self.changes = KeyParameters(changed)
return True
except Exception:
pass
return False
def should_update(self):
result = self._update_changed_options()
if result:
return True
return False
def update_on_device(self):
content = StringIO(self.want.key_content)
self.client.api.shared.file_transfer.uploads.upload_stringio( self.client.api.shared.file_transfer.uploads.upload_stringio(
content, self.want.key_filename content, self.want.filename
)
resource = self.client.api.tm.sys.file.ssl_keys.ssl_key.load(
name=self.want.key_filename,
partition=self.want.partition
) )
resource.update()
def exists(self): resource = self.client.api.tm.sys.file.ssl_certs.ssl_cert.create(
result = self.client.api.tm.sys.file.ssl_keys.ssl_key.exists( sourcePath=self.want.source_path,
name=self.want.key_filename, name=self.want.filename,
partition=self.want.partition partition=self.want.partition
) )
return result
def present(self): # This needs to be done because of the way that BIG-IP creates certificates.
if self.want.key_content is None: #
return False # The extra params (such as OCSP and issuer stuff) are not available in the
return super(KeyManager, self).present() # payload. In a nutshell, the available resource attributes *change* after
# a create so that *more* are available.
params = self.want.api_params()
if params:
resource.update(**params)
def read_current_from_device(self): def read_current_from_device(self):
resource = self.client.api.tm.sys.file.ssl_keys.ssl_key.load( resource = self.client.api.tm.sys.file.ssl_certs.ssl_cert.load(
name=self.want.key_filename, name=self.want.filename,
partition=self.want.partition partition=self.want.partition,
) requests_params=dict(
result = resource.attrs params='expandSubcollections=true'
return KeyParameters(result)
def create_on_device(self):
content = StringIO(self.want.key_content)
self.client.api.shared.file_transfer.uploads.upload_stringio(
content, self.want.key_filename
) )
self.client.api.tm.sys.file.ssl_keys.ssl_key.create(
sourcePath=self.want.key_source_path,
name=self.want.key_filename,
partition=self.want.partition
) )
result = resource.attrs
return ApiParameters(params=result)
def remove_from_device(self): def remove_from_device(self):
resource = self.client.api.tm.sys.file.ssl_keys.ssl_key.load( resource = self.client.api.tm.sys.file.ssl_certs.ssl_cert.load(
name=self.want.key_filename, name=self.want.filename,
partition=self.want.partition partition=self.want.partition
) )
resource.delete() resource.delete()
def remove(self):
result = super(KeyManager, self).remove()
if self.exists() and not self.client.check_mode:
raise F5ModuleError("Failed to delete the key")
return result
class ArgumentSpec(object): class ArgumentSpec(object):
def __init__(self): def __init__(self):
self.supports_check_mode = True self.supports_check_mode = True
self.argument_spec = dict( argument_spec = dict(
name=dict( name=dict(
required=True required=True
), ),
cert_content=dict(aliases=['content']), content=dict(aliases=['cert_content']),
cert_src=dict(
type='path',
removed_in_version='2.4'
),
key_content=dict(),
key_src=dict(
type='path',
removed_in_version='2.4'
),
passphrase=dict(
no_log=True
),
state=dict( state=dict(
default='present', default='present',
choices=['absent', 'present'] choices=['absent', 'present']
),
issuer_cert=dict(),
partition=dict(
default='Common',
fallback=(env_fallback, ['F5_PARTITION'])
) )
) )
self.mutually_exclusive = [ self.argument_spec = {}
['key_content', 'key_src'], self.argument_spec.update(f5_argument_spec)
['cert_content', 'cert_src'] self.argument_spec.update(argument_spec)
]
self.f5_product_name = 'bigip'
def main(): def main():
if not HAS_F5SDK:
raise F5ModuleError("The python f5-sdk module is required")
spec = ArgumentSpec() spec = ArgumentSpec()
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
mutually_exclusive=spec.mutually_exclusive, supports_check_mode=spec.supports_check_mode
supports_check_mode=spec.supports_check_mode,
f5_product_name=spec.f5_product_name
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
try: try:
mm = ModuleManager(client) client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
client.module.exit_json(**results) cleanup_tokens(client)
except F5ModuleError as e: module.exit_json(**results)
client.module.fail_json(msg=str(e)) except F5ModuleError as ex:
cleanup_tokens(client)
module.fail_json(msg=str(ex))
if __name__ == '__main__': if __name__ == '__main__':

@ -43,9 +43,12 @@ options:
passphrase: passphrase:
description: description:
- Passphrase on key. - Passphrase on key.
partition:
description:
- Device partition to manage resources on.
default: Common
version_added: 2.5
notes: notes:
- Requires the f5-sdk Python package on the host. This is as easy as pip
install f5-sdk.
- This module does not behave like other modules that you might include in - This module does not behave like other modules that you might include in
roles where referencing files or templates first looks in the role's roles where referencing files or templates first looks in the role's
files or templates directory. To have it behave that way, use the Ansible files or templates directory. To have it behave that way, use the Ansible
@ -53,7 +56,6 @@ notes:
a role context. a role context.
extends_documentation_fragment: f5 extends_documentation_fragment: f5
requirements: requirements:
- f5-sdk >= 1.5.0
- BIG-IP >= v12 - BIG-IP >= v12
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
@ -101,26 +103,48 @@ key_source_path:
sample: /var/config/rest/downloads/cert1.key sample: /var/config/rest/downloads/cert1.key
''' '''
import hashlib import hashlib
import os import os
import re import re
try: from ansible.module_utils.basic import AnsibleModule
from StringIO import StringIO from ansible.module_utils.basic import env_fallback
except ImportError:
from io import StringIO
from ansible.module_utils.f5_utils import AnsibleF5Client HAS_DEVEL_IMPORTS = False
from ansible.module_utils.f5_utils import AnsibleF5Parameters
from ansible.module_utils.f5_utils import HAS_F5SDK
from ansible.module_utils.f5_utils import F5ModuleError
try: try:
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError # Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK
from library.module_utils.network.f5.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens
from library.module_utils.network.f5.common import fqdn_name
from library.module_utils.network.f5.common import f5_argument_spec
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
HAS_DEVEL_IMPORTS = True
except ImportError: except ImportError:
# Upstream Ansible
from ansible.module_utils.network.f5.bigip import HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client
from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters
from ansible.module_utils.network.f5.common import cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
download_path = '/var/config/rest/downloads' download_path = '/var/config/rest/downloads'
@ -145,16 +169,6 @@ class Parameters(AnsibleF5Parameters):
pass pass
return result return result
def api_params(self):
result = {}
for api_attribute in self.api_attributes:
if self.api_map is not None and api_attribute in self.api_map:
result[api_attribute] = getattr(self, self.api_map[api_attribute])
else:
result[api_attribute] = getattr(self, api_attribute)
result = self._filter_params(result)
return result
def _get_hash(self, content): def _get_hash(self, content):
k = hashlib.sha1() k = hashlib.sha1()
s = StringIO(content) s = StringIO(content)
@ -198,12 +212,17 @@ class Parameters(AnsibleF5Parameters):
return None return None
class Changes(Parameters):
pass
class ModuleManager(object): class ModuleManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None)
self.have = None self.have = None
self.want = Parameters(self.client.module.params) self.want = Parameters(params=self.module.params)
self.changes = Parameters() self.changes = Changes()
def exec_module(self): def exec_module(self):
changed = False changed = False
@ -230,7 +249,7 @@ class ModuleManager(object):
if getattr(self.want, key) is not None: if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key) changed[key] = getattr(self.want, key)
if changed: if changed:
self.changes = Parameters(changed) self.changes = Changes(params=changed)
except Exception: except Exception:
pass pass
@ -246,7 +265,7 @@ class ModuleManager(object):
if self.want.key_checksum != self.have.checksum: if self.want.key_checksum != self.have.checksum:
changed['key_checksum'] = self.want.key_checksum changed['key_checksum'] = self.want.key_checksum
if changed: if changed:
self.changes = Parameters(changed) self.changes = Changes(params=changed)
return True return True
except Exception: except Exception:
pass pass
@ -286,7 +305,7 @@ class ModuleManager(object):
if self.want.content is None: if self.want.content is None:
return False return False
self._set_changed_options() self._set_changed_options()
if self.client.check_mode: if self.module.check_mode:
return True return True
self.create_on_device() self.create_on_device()
return True return True
@ -297,13 +316,13 @@ class ModuleManager(object):
partition=self.want.partition partition=self.want.partition
) )
result = resource.attrs result = resource.attrs
return Parameters(result) return Parameters(params=result)
def update(self): def update(self):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
if not self.should_update(): if not self.should_update():
return False return False
if self.client.check_mode: if self.module.check_mode:
return True return True
self.update_on_device() self.update_on_device()
return True return True
@ -332,7 +351,7 @@ class ModuleManager(object):
resource.delete() resource.delete()
def remove(self): def remove(self):
if self.client.check_mode: if self.module.check_mode:
return True return True
self.remove_from_device() self.remove_from_device()
if self.exists(): if self.exists():
@ -343,7 +362,7 @@ class ModuleManager(object):
class ArgumentSpec(object): class ArgumentSpec(object):
def __init__(self): def __init__(self):
self.supports_check_mode = True self.supports_check_mode = True
self.argument_spec = dict( argument_spec = dict(
name=dict( name=dict(
required=True required=True
), ),
@ -357,29 +376,36 @@ class ArgumentSpec(object):
required=False, required=False,
default='present', default='present',
choices=['absent', 'present'] choices=['absent', 'present']
),
partition=dict(
default='Common',
fallback=(env_fallback, ['F5_PARTITION'])
) )
) )
self.f5_product_name = 'bigip' self.argument_spec = {}
self.argument_spec.update(f5_argument_spec)
self.argument_spec.update(argument_spec)
def main(): def main():
if not HAS_F5SDK:
raise F5ModuleError("The python f5-sdk module is required")
spec = ArgumentSpec() spec = ArgumentSpec()
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=spec.supports_check_mode, supports_check_mode=spec.supports_check_mode
f5_product_name=spec.f5_product_name
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
try: try:
mm = ModuleManager(client) client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
client.module.exit_json(**results) cleanup_tokens(client)
except F5ModuleError as e: module.exit_json(**results)
client.module.fail_json(msg=str(e)) except F5ModuleError as ex:
cleanup_tokens(client)
module.fail_json(msg=str(ex))
if __name__ == '__main__': if __name__ == '__main__':

@ -41,19 +41,6 @@ lib/ansible/modules/net_tools/cloudflare_dns.py E317
lib/ansible/modules/net_tools/haproxy.py E317 lib/ansible/modules/net_tools/haproxy.py E317
lib/ansible/modules/net_tools/omapi_host.py E317 lib/ansible/modules/net_tools/omapi_host.py E317
lib/ansible/modules/network/cloudengine/ce_reboot.py E317 lib/ansible/modules/network/cloudengine/ce_reboot.py E317
lib/ansible/modules/network/f5/bigip_pool.py E321
lib/ansible/modules/network/f5/bigip_profile_client_ssl.py E321
lib/ansible/modules/network/f5/bigip_provision.py E321
lib/ansible/modules/network/f5/bigip_qkview.py E321
lib/ansible/modules/network/f5/bigip_remote_syslog.py E321
lib/ansible/modules/network/f5/bigip_security_port_list.py E321
lib/ansible/modules/network/f5/bigip_selfip.py E321
lib/ansible/modules/network/f5/bigip_snat_pool.py E321
lib/ansible/modules/network/f5/bigip_snmp.py E321
lib/ansible/modules/network/f5/bigip_snmp_trap.py E321
lib/ansible/modules/network/f5/bigip_software_update.py E321
lib/ansible/modules/network/f5/bigip_ssl_certificate.py E321
lib/ansible/modules/network/f5/bigip_ssl_key.py E321
lib/ansible/modules/network/f5/bigip_sys_db.py E321 lib/ansible/modules/network/f5/bigip_sys_db.py E321
lib/ansible/modules/network/f5/bigip_sys_global.py E321 lib/ansible/modules/network/f5/bigip_sys_global.py E321
lib/ansible/modules/network/f5/bigip_traffic_group.py E321 lib/ansible/modules/network/f5/bigip_traffic_group.py E321

@ -0,0 +1,42 @@
{
"kind": "tm:sys:file:ssl-cert:ssl-certstate",
"name": "ocsp.example.com.crt",
"partition": "Common",
"fullPath": "/Common/ocsp.example.com.crt",
"generation": 548,
"selfLink": "https://localhost/mgmt/tm/sys/file/ssl-cert/~Common~ocsp.example.com.crt?ver=13.0.0",
"certValidationOptions": [
"ocsp"
],
"certificateKeyCurveName": "none",
"certificateKeySize": 4096,
"checksum": "SHA1:2113:b84ae5ed7e236b35206b2ff61289df9547d5afa3",
"createTime": "2017-10-25T20:56:09Z",
"createdBy": "admin",
"expirationDate": 1545199767,
"expirationString": "Dec 19 06:09:27 2018 GMT",
"isBundle": "false",
"issuer": "CN=Alice Ltd Intermediate CA,OU=Alice Ltd Certificate Authority,O=Alice Ltd,ST=England,C=GB",
"issuerCert": "/Common/intermediate.crt",
"issuerCertReference": {
"link": "https://localhost/mgmt/tm/sys/file/ssl-cert/~Common~intermediate.crt?ver=13.0.0"
},
"keyType": "rsa-public",
"lastUpdateTime": "2017-10-25T20:56:09Z",
"mode": 33188,
"revision": 1,
"serialNumber": "4096",
"size": 2113,
"sourcePath": "file:///var/config/rest/downloads/ocsp.example.com.crt",
"subject": "CN=ocsp.example.com,OU=Alice Ltd Certificate Authority,O=Alice Ltd,ST=England,C=GB",
"updatedBy": "admin",
"version": 3,
"bundleCertificatesReference": {
"link": "https://localhost/mgmt/tm/sys/file/ssl-cert/~Common~ocsp.example.com.crt/bundle-certificates?ver=13.0.0",
"isSubcollection": true
},
"certValidatorsReference": {
"link": "https://localhost/mgmt/tm/sys/file/ssl-cert/~Common~ocsp.example.com.crt/cert-validators?ver=13.0.0",
"isSubcollection": true
}
}

@ -18,15 +18,15 @@ if sys.version_info < (2, 7):
from ansible.compat.tests import unittest from ansible.compat.tests import unittest
from ansible.compat.tests.mock import Mock from ansible.compat.tests.mock import Mock
from ansible.compat.tests.mock import patch from ansible.compat.tests.mock import patch
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.f5_utils import F5ModuleError
try: try:
from library.bigip_pool import ApiParameters from library.bigip_pool import ApiParameters
from library.bigip_pool import ModuleParameters from library.bigip_pool import ModuleParameters
from library.bigip_pool import ModuleManager from library.bigip_pool import ModuleManager
from library.bigip_pool import ArgumentSpec from library.bigip_pool import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
from test.unit.modules.utils import set_module_args from test.unit.modules.utils import set_module_args
except ImportError: except ImportError:
try: try:
@ -34,7 +34,8 @@ except ImportError:
from ansible.modules.network.f5.bigip_pool import ModuleParameters from ansible.modules.network.f5.bigip_pool import ModuleParameters
from ansible.modules.network.f5.bigip_pool import ModuleManager from ansible.modules.network.f5.bigip_pool import ModuleManager
from ansible.modules.network.f5.bigip_pool import ArgumentSpec from ansible.modules.network.f5.bigip_pool import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
except ImportError: except ImportError:
raise SkipTest("F5 Ansible modules require the f5-sdk Python library") raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
@ -72,7 +73,7 @@ class TestParameters(unittest.TestCase):
service_down_action='drop' service_down_action='drop'
) )
p = ModuleParameters(args) p = ModuleParameters(params=args)
assert p.monitor_type == 'm_of_n' assert p.monitor_type == 'm_of_n'
assert p.quorum == 1 assert p.quorum == 1
assert p.monitors == 'min 1 of { /Common/Fake /Common/Fake2 }' assert p.monitors == 'min 1 of { /Common/Fake /Common/Fake2 }'
@ -88,7 +89,7 @@ class TestParameters(unittest.TestCase):
serviceDownAction='drop' serviceDownAction='drop'
) )
p = ApiParameters(args) p = ApiParameters(params=args)
assert p.monitors == '/Common/Fake and /Common/Fake2' assert p.monitors == '/Common/Fake and /Common/Fake2'
assert p.slow_ramp_time == 200 assert p.slow_ramp_time == 200
assert p.reselect_tries == 5 assert p.reselect_tries == 5
@ -99,7 +100,7 @@ class TestParameters(unittest.TestCase):
lb_method='obscure_hyphenated_fake_method', lb_method='obscure_hyphenated_fake_method',
) )
with pytest.raises(F5ModuleError): with pytest.raises(F5ModuleError):
p = ModuleParameters(args) p = ModuleParameters(params=args)
assert p.lb_method == 'foo' assert p.lb_method == 'foo'
def test_unknown_api_lb_method(self): def test_unknown_api_lb_method(self):
@ -107,12 +108,10 @@ class TestParameters(unittest.TestCase):
loadBalancingMode='obscure_hypenated_fake_method' loadBalancingMode='obscure_hypenated_fake_method'
) )
with pytest.raises(F5ModuleError): with pytest.raises(F5ModuleError):
p = ApiParameters(args) p = ApiParameters(params=args)
assert p.lb_method == 'foo' assert p.lb_method == 'foo'
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestManager(unittest.TestCase): class TestManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -132,13 +131,12 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)
@ -163,13 +161,12 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)
@ -191,13 +188,12 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)
@ -220,13 +216,12 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)
@ -247,13 +242,12 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)
@ -276,13 +270,12 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)
@ -304,14 +297,13 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
current = ApiParameters(load_fixture('load_ltm_pool.json')) current = ApiParameters(params=load_fixture('load_ltm_pool.json'))
mm.update_on_device = Mock(return_value=True) mm.update_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=True) mm.exists = Mock(return_value=True)
@ -332,13 +324,12 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)
@ -360,13 +351,12 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)
@ -388,13 +378,12 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)
@ -417,13 +406,12 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)
@ -443,13 +431,12 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)

@ -18,14 +18,15 @@ if sys.version_info < (2, 7):
from ansible.compat.tests import unittest from ansible.compat.tests import unittest
from ansible.compat.tests.mock import Mock from ansible.compat.tests.mock import Mock
from ansible.compat.tests.mock import patch from ansible.compat.tests.mock import patch
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_profile_client_ssl import ModuleParameters from library.bigip_profile_client_ssl import ModuleParameters
from library.bigip_profile_client_ssl import ApiParameters from library.bigip_profile_client_ssl import ApiParameters
from library.bigip_profile_client_ssl import ModuleManager from library.bigip_profile_client_ssl import ModuleManager
from library.bigip_profile_client_ssl import ArgumentSpec from library.bigip_profile_client_ssl import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
from test.unit.modules.utils import set_module_args from test.unit.modules.utils import set_module_args
except ImportError: except ImportError:
try: try:
@ -33,7 +34,8 @@ except ImportError:
from ansible.modules.network.f5.bigip_profile_client_ssl import ApiParameters from ansible.modules.network.f5.bigip_profile_client_ssl import ApiParameters
from ansible.modules.network.f5.bigip_profile_client_ssl import ModuleManager from ansible.modules.network.f5.bigip_profile_client_ssl import ModuleManager
from ansible.modules.network.f5.bigip_profile_client_ssl import ArgumentSpec from ansible.modules.network.f5.bigip_profile_client_ssl import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
except ImportError: except ImportError:
raise SkipTest("F5 Ansible modules require the f5-sdk Python library") raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
@ -75,20 +77,18 @@ class TestParameters(unittest.TestCase):
] ]
) )
p = ModuleParameters(args) p = ModuleParameters(params=args)
assert p.name == 'foo' assert p.name == 'foo'
assert p.parent == '/Common/bar' assert p.parent == '/Common/bar'
assert p.ciphers == '!SSLv3:!SSLv2:ECDHE+AES-GCM+SHA256:ECDHE-RSA-AES128-CBC-SHA' assert p.ciphers == '!SSLv3:!SSLv2:ECDHE+AES-GCM+SHA256:ECDHE-RSA-AES128-CBC-SHA'
def test_api_parameters(self): def test_api_parameters(self):
args = load_fixture('load_ltm_profile_clientssl.json') args = load_fixture('load_ltm_profile_clientssl.json')
p = ApiParameters(args) p = ApiParameters(params=args)
assert p.name == 'foo' assert p.name == 'foo'
assert p.ciphers == 'DEFAULT' assert p.ciphers == 'DEFAULT'
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestManager(unittest.TestCase): class TestManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -112,12 +112,11 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
mm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)

@ -17,20 +17,22 @@ if sys.version_info < (2, 7):
from ansible.compat.tests import unittest from ansible.compat.tests import unittest
from ansible.compat.tests.mock import Mock from ansible.compat.tests.mock import Mock
from ansible.compat.tests.mock import patch from ansible.compat.tests.mock import patch
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_provision import Parameters from library.bigip_provision import Parameters
from library.bigip_provision import ModuleManager from library.bigip_provision import ModuleManager
from library.bigip_provision import ArgumentSpec from library.bigip_provision import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
from test.unit.modules.utils import set_module_args from test.unit.modules.utils import set_module_args
except ImportError: except ImportError:
try: try:
from ansible.modules.network.f5.bigip_provision import Parameters from ansible.modules.network.f5.bigip_provision import Parameters
from ansible.modules.network.f5.bigip_provision import ModuleManager from ansible.modules.network.f5.bigip_provision import ModuleManager
from ansible.modules.network.f5.bigip_provision import ArgumentSpec from ansible.modules.network.f5.bigip_provision import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
except ImportError: except ImportError:
raise SkipTest("F5 Ansible modules require the f5-sdk Python library") raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
@ -65,12 +67,10 @@ class TestParameters(unittest.TestCase):
server='localhost', server='localhost',
user='admin' user='admin'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.module == 'gtm' assert p.module == 'gtm'
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestManager(unittest.TestCase): class TestManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -93,12 +93,11 @@ class TestManager(unittest.TestCase):
level='none' level='none'
) )
) )
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
mm.update_on_device = Mock(return_value=True) mm.update_on_device = Mock(return_value=True)
@ -130,9 +129,8 @@ class TestManager(unittest.TestCase):
)) ))
with patch('ansible.module_utils.basic.AnsibleModule.fail_json') as mo: with patch('ansible.module_utils.basic.AnsibleModule.fail_json') as mo:
AnsibleF5Client( AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode,
f5_product_name=self.spec.f5_product_name
) )
mo.assert_not_called() mo.assert_not_called()

@ -17,7 +17,7 @@ if sys.version_info < (2, 7):
from ansible.compat.tests import unittest from ansible.compat.tests import unittest
from ansible.compat.tests.mock import Mock from ansible.compat.tests.mock import Mock
from ansible.compat.tests.mock import patch from ansible.compat.tests.mock import patch
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_qkview import Parameters from library.bigip_qkview import Parameters
@ -25,7 +25,8 @@ try:
from library.bigip_qkview import MadmLocationManager from library.bigip_qkview import MadmLocationManager
from library.bigip_qkview import BulkLocationManager from library.bigip_qkview import BulkLocationManager
from library.bigip_qkview import ArgumentSpec from library.bigip_qkview import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
from test.unit.modules.utils import set_module_args from test.unit.modules.utils import set_module_args
except ImportError: except ImportError:
try: try:
@ -34,7 +35,8 @@ except ImportError:
from ansible.modules.network.f5.bigip_qkview import MadmLocationManager from ansible.modules.network.f5.bigip_qkview import MadmLocationManager
from ansible.modules.network.f5.bigip_qkview import BulkLocationManager from ansible.modules.network.f5.bigip_qkview import BulkLocationManager
from ansible.modules.network.f5.bigip_qkview import ArgumentSpec from ansible.modules.network.f5.bigip_qkview import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
except ImportError: except ImportError:
raise SkipTest("F5 Ansible modules require the f5-sdk Python library") raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
@ -73,7 +75,7 @@ class TestParameters(unittest.TestCase):
exclude=['audit', 'secure'], exclude=['audit', 'secure'],
dest='/tmp/foo.qkview' dest='/tmp/foo.qkview'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.filename == 'foo.qkview' assert p.filename == 'foo.qkview'
assert p.asm_request_log is None assert p.asm_request_log is None
assert p.max_file_size == '-s 1024' assert p.max_file_size == '-s 1024'
@ -89,12 +91,10 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
asm_request_log=True, asm_request_log=True,
) )
p = Parameters(args) p = Parameters(params=args)
assert p.asm_request_log == '-o asm-request-log' assert p.asm_request_log == '-o asm-request-log'
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestMadmLocationManager(unittest.TestCase): class TestMadmLocationManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -108,14 +108,13 @@ class TestMadmLocationManager(unittest.TestCase):
password='password' password='password'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
# Override methods in the specific type of manager # Override methods in the specific type of manager
tm = MadmLocationManager(client) tm = MadmLocationManager(module=module, params=module.params)
tm.exists = Mock(return_value=False) tm.exists = Mock(return_value=False)
tm.execute_on_device = Mock(return_value=True) tm.execute_on_device = Mock(return_value=True)
tm._move_qkview_to_download = Mock(return_value=True) tm._move_qkview_to_download = Mock(return_value=True)
@ -123,7 +122,7 @@ class TestMadmLocationManager(unittest.TestCase):
tm._delete_qkview = Mock(return_value=True) tm._delete_qkview = Mock(return_value=True)
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.is_version_less_than_14 = Mock(return_value=True) mm.is_version_less_than_14 = Mock(return_value=True)
mm.get_manager = Mock(return_value=tm) mm.get_manager = Mock(return_value=tm)
@ -134,8 +133,6 @@ class TestMadmLocationManager(unittest.TestCase):
assert results['changed'] is False assert results['changed'] is False
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestBulkLocationManager(unittest.TestCase): class TestBulkLocationManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -149,14 +146,13 @@ class TestBulkLocationManager(unittest.TestCase):
password='password' password='password'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
# Override methods in the specific type of manager # Override methods in the specific type of manager
tm = BulkLocationManager(client) tm = BulkLocationManager(module=module, params=module.params)
tm.exists = Mock(return_value=False) tm.exists = Mock(return_value=False)
tm.execute_on_device = Mock(return_value=True) tm.execute_on_device = Mock(return_value=True)
tm._move_qkview_to_download = Mock(return_value=True) tm._move_qkview_to_download = Mock(return_value=True)
@ -164,7 +160,7 @@ class TestBulkLocationManager(unittest.TestCase):
tm._delete_qkview = Mock(return_value=True) tm._delete_qkview = Mock(return_value=True)
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.is_version_less_than_14 = Mock(return_value=False) mm.is_version_less_than_14 = Mock(return_value=False)
mm.get_manager = Mock(return_value=tm) mm.get_manager = Mock(return_value=tm)

@ -17,7 +17,7 @@ if sys.version_info < (2, 7):
from ansible.compat.tests import unittest from ansible.compat.tests import unittest
from ansible.compat.tests.mock import Mock from ansible.compat.tests.mock import Mock
from ansible.compat.tests.mock import patch from ansible.compat.tests.mock import patch
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_remote_syslog import Parameters from library.bigip_remote_syslog import Parameters
@ -25,7 +25,8 @@ try:
from library.bigip_remote_syslog import ArgumentSpec from library.bigip_remote_syslog import ArgumentSpec
from library.bigip_remote_syslog import HAS_F5SDK from library.bigip_remote_syslog import HAS_F5SDK
from library.bigip_remote_syslog import HAS_NETADDR from library.bigip_remote_syslog import HAS_NETADDR
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
from test.unit.modules.utils import set_module_args from test.unit.modules.utils import set_module_args
except ImportError: except ImportError:
try: try:
@ -34,7 +35,8 @@ except ImportError:
from ansible.modules.network.f5.bigip_remote_syslog import ArgumentSpec from ansible.modules.network.f5.bigip_remote_syslog import ArgumentSpec
from ansible.modules.network.f5.bigip_remote_syslog import HAS_F5SDK from ansible.modules.network.f5.bigip_remote_syslog import HAS_F5SDK
from ansible.modules.network.f5.bigip_remote_syslog import HAS_NETADDR from ansible.modules.network.f5.bigip_remote_syslog import HAS_NETADDR
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
except ImportError: except ImportError:
raise SkipTest("F5 Ansible modules require the f5-sdk Python library") raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
@ -73,7 +75,7 @@ class TestParameters(unittest.TestCase):
local_ip='1.1.1.1' local_ip='1.1.1.1'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.remote_host == '10.10.10.10' assert p.remote_host == '10.10.10.10'
assert p.remote_port == 514 assert p.remote_port == 514
assert p.local_ip == '1.1.1.1' assert p.local_ip == '1.1.1.1'
@ -90,12 +92,10 @@ class TestParameters(unittest.TestCase):
] ]
) )
p = Parameters(args) p = Parameters(params=args)
assert len(p.remoteServers) == 1 assert len(p.remoteServers) == 1
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestManager(unittest.TestCase): class TestManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -109,14 +109,13 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
# Override methods in the specific type of manager # Override methods in the specific type of manager
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.exists = Mock(side_effect=[False, True]) mm.exists = Mock(side_effect=[False, True])
mm.update_on_device = Mock(return_value=True) mm.update_on_device = Mock(return_value=True)
@ -132,15 +131,14 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
current = Parameters(load_fixture('load_tm_sys_syslog.json')) current = Parameters(params=load_fixture('load_tm_sys_syslog.json'))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
# Override methods in the specific type of manager # Override methods in the specific type of manager
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.exists = Mock(return_value=True) mm.exists = Mock(return_value=True)
mm.read_current_from_device = Mock(return_value=current) mm.read_current_from_device = Mock(return_value=current)
@ -157,15 +155,14 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
current = Parameters(load_fixture('load_tm_sys_syslog.json')) current = Parameters(params=load_fixture('load_tm_sys_syslog.json'))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
# Override methods in the specific type of manager # Override methods in the specific type of manager
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.exists = Mock(return_value=True) mm.exists = Mock(return_value=True)
mm.read_current_from_device = Mock(return_value=current) mm.read_current_from_device = Mock(return_value=current)
mm.update_on_device = Mock(return_value=True) mm.update_on_device = Mock(return_value=True)
@ -184,15 +181,14 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
current = Parameters(load_fixture('load_tm_sys_syslog.json')) current = Parameters(params=load_fixture('load_tm_sys_syslog.json'))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
# Override methods in the specific type of manager # Override methods in the specific type of manager
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.exists = Mock(return_value=True) mm.exists = Mock(return_value=True)
mm.read_current_from_device = Mock(return_value=current) mm.read_current_from_device = Mock(return_value=current)
mm.update_on_device = Mock(return_value=True) mm.update_on_device = Mock(return_value=True)

@ -18,15 +18,15 @@ if sys.version_info < (2, 7):
from ansible.compat.tests import unittest from ansible.compat.tests import unittest
from ansible.compat.tests.mock import Mock from ansible.compat.tests.mock import Mock
from ansible.compat.tests.mock import patch from ansible.compat.tests.mock import patch
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.f5_utils import F5ModuleError
try: try:
from library.bigip_security_port_list import ApiParameters from library.bigip_security_port_list import ApiParameters
from library.bigip_security_port_list import ModuleParameters from library.bigip_security_port_list import ModuleParameters
from library.bigip_security_port_list import ModuleManager from library.bigip_security_port_list import ModuleManager
from library.bigip_security_port_list import ArgumentSpec from library.bigip_security_port_list import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
from test.unit.modules.utils import set_module_args from test.unit.modules.utils import set_module_args
except ImportError: except ImportError:
try: try:
@ -35,7 +35,8 @@ except ImportError:
from ansible.modules.network.f5.bigip_security_port_list import ModuleParameters from ansible.modules.network.f5.bigip_security_port_list import ModuleParameters
from ansible.modules.network.f5.bigip_security_port_list import ModuleManager from ansible.modules.network.f5.bigip_security_port_list import ModuleManager
from ansible.modules.network.f5.bigip_security_port_list import ArgumentSpec from ansible.modules.network.f5.bigip_security_port_list import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
except ImportError: except ImportError:
raise SkipTest("F5 Ansible modules require the f5-sdk Python library") raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
@ -72,7 +73,7 @@ class TestParameters(unittest.TestCase):
port_lists=['/Common/foo', 'foo'] port_lists=['/Common/foo', 'foo']
) )
p = ModuleParameters(args) p = ModuleParameters(params=args)
assert p.name == 'foo' assert p.name == 'foo'
assert p.description == 'this is a description' assert p.description == 'this is a description'
assert len(p.ports) == 4 assert len(p.ports) == 4
@ -82,7 +83,7 @@ class TestParameters(unittest.TestCase):
def test_api_parameters(self): def test_api_parameters(self):
args = load_fixture('load_security_port_list_1.json') args = load_fixture('load_security_port_list_1.json')
p = ApiParameters(args) p = ApiParameters(params=args)
assert len(p.ports) == 4 assert len(p.ports) == 4
assert len(p.port_ranges) == 3 assert len(p.port_ranges) == 3
assert len(p.port_lists) == 1 assert len(p.port_lists) == 1
@ -91,8 +92,6 @@ class TestParameters(unittest.TestCase):
assert p.port_lists[0] == '/Common/_sys_self_allow_tcp_defaults' assert p.port_lists[0] == '/Common/_sys_self_allow_tcp_defaults'
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestManager(unittest.TestCase): class TestManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -105,17 +104,16 @@ class TestManager(unittest.TestCase):
ports=[1, 2, 3, 4], ports=[1, 2, 3, 4],
port_ranges=['10-20', '30-40', '50-60'], port_ranges=['10-20', '30-40', '50-60'],
port_lists=['/Common/foo', 'foo'], port_lists=['/Common/foo', 'foo'],
password='passsword', password='password',
server='localhost', server='localhost',
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
mm.exists = Mock(side_effect=[False, True]) mm.exists = Mock(side_effect=[False, True])

@ -18,15 +18,15 @@ if sys.version_info < (2, 7):
from ansible.compat.tests import unittest from ansible.compat.tests import unittest
from ansible.compat.tests.mock import Mock from ansible.compat.tests.mock import Mock
from ansible.compat.tests.mock import patch from ansible.compat.tests.mock import patch
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.f5_utils import F5ModuleError
try: try:
from library.bigip_selfip import Parameters from library.bigip_selfip import Parameters
from library.bigip_selfip import ApiParameters from library.bigip_selfip import ApiParameters
from library.bigip_selfip import ModuleManager from library.bigip_selfip import ModuleManager
from library.bigip_selfip import ArgumentSpec from library.bigip_selfip import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
from test.unit.modules.utils import set_module_args from test.unit.modules.utils import set_module_args
except ImportError: except ImportError:
try: try:
@ -34,7 +34,8 @@ except ImportError:
from ansible.modules.network.f5.bigip_selfip import ApiParameters from ansible.modules.network.f5.bigip_selfip import ApiParameters
from ansible.modules.network.f5.bigip_selfip import ModuleManager from ansible.modules.network.f5.bigip_selfip import ModuleManager
from ansible.modules.network.f5.bigip_selfip import ArgumentSpec from ansible.modules.network.f5.bigip_selfip import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
except ImportError: except ImportError:
raise SkipTest("F5 Ansible modules require the f5-sdk Python library") raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
@ -78,9 +79,9 @@ class TestParameters(unittest.TestCase):
traffic_group='traffic-group-local-only', traffic_group='traffic-group-local-only',
vlan='net1' vlan='net1'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.address == '10.10.10.10%1/24' assert p.address == '10.10.10.10%1/24'
assert p.allow_service == set(['tcp:80', 'udp:53', 'gre:0']) assert p.allow_service == ['gre:0', 'tcp:80', 'udp:53']
assert p.name == 'net1' assert p.name == 'net1'
assert p.netmask == 24 assert p.netmask == 24
assert p.route_domain == 1 assert p.route_domain == 1
@ -95,9 +96,9 @@ class TestParameters(unittest.TestCase):
'grp' 'grp'
] ]
) )
p = Parameters(args) p = Parameters(params=args)
with pytest.raises(F5ModuleError) as ex: with pytest.raises(F5ModuleError) as ex:
assert p.allow_service == set(['tcp:80', 'udp:53', 'grp']) assert p.allow_service == ['grp', 'tcp:80', 'udp:53']
assert 'The provided protocol' in str(ex) assert 'The provided protocol' in str(ex)
def test_api_parameters(self): def test_api_parameters(self):
@ -113,9 +114,9 @@ class TestParameters(unittest.TestCase):
trafficGroup='/Common/traffic-group-local-only', trafficGroup='/Common/traffic-group-local-only',
vlan='net1' vlan='net1'
) )
p = ApiParameters(args) p = ApiParameters(params=args)
assert p.address == '10.10.10.10%1/24' assert p.address == '10.10.10.10%1/24'
assert p.allow_service == set(['tcp:80', 'udp:53', 'gre']) assert p.allow_service == ['gre', 'tcp:80', 'udp:53']
assert p.name == 'net1' assert p.name == 'net1'
assert p.netmask == 24 assert p.netmask == 24
assert p.route_domain == 1 assert p.route_domain == 1
@ -123,8 +124,6 @@ class TestParameters(unittest.TestCase):
assert p.vlan == '/Common/net1' assert p.vlan == '/Common/net1'
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestManager(unittest.TestCase): class TestManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -150,12 +149,11 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
mm.exists = Mock(side_effect=[False, True]) mm.exists = Mock(side_effect=[False, True])
@ -185,14 +183,13 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
current = ApiParameters(load_fixture('load_tm_net_self.json')) current = ApiParameters(params=load_fixture('load_tm_net_self.json'))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
mm.exists = Mock(side_effect=[True, True]) mm.exists = Mock(side_effect=[True, True])

@ -17,20 +17,22 @@ if sys.version_info < (2, 7):
from ansible.compat.tests import unittest from ansible.compat.tests import unittest
from ansible.compat.tests.mock import Mock from ansible.compat.tests.mock import Mock
from ansible.compat.tests.mock import patch from ansible.compat.tests.mock import patch
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_snat_pool import Parameters from library.bigip_snat_pool import Parameters
from library.bigip_snat_pool import ModuleManager from library.bigip_snat_pool import ModuleManager
from library.bigip_snat_pool import ArgumentSpec from library.bigip_snat_pool import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
from test.unit.modules.utils import set_module_args from test.unit.modules.utils import set_module_args
except ImportError: except ImportError:
try: try:
from ansible.modules.network.f5.bigip_snat_pool import Parameters from ansible.modules.network.f5.bigip_snat_pool import Parameters
from ansible.modules.network.f5.bigip_snat_pool import ModuleManager from ansible.modules.network.f5.bigip_snat_pool import ModuleManager
from ansible.modules.network.f5.bigip_snat_pool import ArgumentSpec from ansible.modules.network.f5.bigip_snat_pool import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
except ImportError: except ImportError:
raise SkipTest("F5 Ansible modules require the f5-sdk Python library") raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
@ -65,7 +67,7 @@ class TestParameters(unittest.TestCase):
members=['10.10.10.10', '20.20.20.20'], members=['10.10.10.10', '20.20.20.20'],
partition='Common' partition='Common'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.name == 'my-snat-pool' assert p.name == 'my-snat-pool'
assert p.state == 'present' assert p.state == 'present'
assert len(p.members) == 2 assert len(p.members) == 2
@ -76,14 +78,12 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
members=['/Common/10.10.10.10', '/foo/20.20.20.20'] members=['/Common/10.10.10.10', '/foo/20.20.20.20']
) )
p = Parameters(args) p = Parameters(params=args)
assert len(p.members) == 2 assert len(p.members) == 2
assert '/Common/10.10.10.10' in p.members assert '/Common/10.10.10.10' in p.members
assert '/Common/20.20.20.20' in p.members assert '/Common/20.20.20.20' in p.members
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestManager(unittest.TestCase): class TestManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -99,12 +99,11 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
mm.exists = Mock(side_effect=[False, True]) mm.exists = Mock(side_effect=[False, True])
@ -127,14 +126,13 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
current = Parameters(load_fixture('load_ltm_snatpool.json')) current = Parameters(params=load_fixture('load_ltm_snatpool.json'))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
mm.exists = Mock(side_effect=[True, True]) mm.exists = Mock(side_effect=[True, True])
@ -154,14 +152,13 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
current = Parameters(load_fixture('load_ltm_snatpool.json')) current = Parameters(params=load_fixture('load_ltm_snatpool.json'))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
mm.read_current_from_device = Mock(return_value=current) mm.read_current_from_device = Mock(return_value=current)

@ -17,20 +17,22 @@ if sys.version_info < (2, 7):
from ansible.compat.tests import unittest from ansible.compat.tests import unittest
from ansible.compat.tests.mock import Mock from ansible.compat.tests.mock import Mock
from ansible.compat.tests.mock import patch from ansible.compat.tests.mock import patch
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_snmp import Parameters from library.bigip_snmp import Parameters
from library.bigip_snmp import ModuleManager from library.bigip_snmp import ModuleManager
from library.bigip_snmp import ArgumentSpec from library.bigip_snmp import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
from test.unit.modules.utils import set_module_args from test.unit.modules.utils import set_module_args
except ImportError: except ImportError:
try: try:
from ansible.modules.network.f5.bigip_snmp import Parameters from ansible.modules.network.f5.bigip_snmp import Parameters
from ansible.modules.network.f5.bigip_snmp import ModuleManager from ansible.modules.network.f5.bigip_snmp import ModuleManager
from ansible.modules.network.f5.bigip_snmp import ArgumentSpec from ansible.modules.network.f5.bigip_snmp import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
except ImportError: except ImportError:
raise SkipTest("F5 Ansible modules require the f5-sdk Python library") raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
@ -69,7 +71,7 @@ class TestParameters(unittest.TestCase):
server='localhost', server='localhost',
user='admin' user='admin'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.agent_status_traps == 'enabled' assert p.agent_status_traps == 'enabled'
assert p.agent_authentication_traps == 'enabled' assert p.agent_authentication_traps == 'enabled'
assert p.device_warning_traps == 'enabled' assert p.device_warning_traps == 'enabled'
@ -85,7 +87,7 @@ class TestParameters(unittest.TestCase):
server='localhost', server='localhost',
user='admin' user='admin'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.agent_status_traps == 'disabled' assert p.agent_status_traps == 'disabled'
assert p.agent_authentication_traps == 'disabled' assert p.agent_authentication_traps == 'disabled'
assert p.device_warning_traps == 'disabled' assert p.device_warning_traps == 'disabled'
@ -98,7 +100,7 @@ class TestParameters(unittest.TestCase):
sysLocation='Lunar orbit', sysLocation='Lunar orbit',
sysContact='Alice@foo.org', sysContact='Alice@foo.org',
) )
p = Parameters(args) p = Parameters(params=args)
assert p.agent_status_traps == 'enabled' assert p.agent_status_traps == 'enabled'
assert p.agent_authentication_traps == 'enabled' assert p.agent_authentication_traps == 'enabled'
assert p.device_warning_traps == 'enabled' assert p.device_warning_traps == 'enabled'
@ -111,14 +113,12 @@ class TestParameters(unittest.TestCase):
authTrap='disabled', authTrap='disabled',
bigipTraps='disabled', bigipTraps='disabled',
) )
p = Parameters(args) p = Parameters(params=args)
assert p.agent_status_traps == 'disabled' assert p.agent_status_traps == 'disabled'
assert p.agent_authentication_traps == 'disabled' assert p.agent_authentication_traps == 'disabled'
assert p.device_warning_traps == 'disabled' assert p.device_warning_traps == 'disabled'
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestManager(unittest.TestCase): class TestManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -140,12 +140,11 @@ class TestManager(unittest.TestCase):
) )
) )
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
mm.update_on_device = Mock(return_value=True) mm.update_on_device = Mock(return_value=True)

@ -18,7 +18,7 @@ from ansible.compat.tests import unittest
from ansible.compat.tests.mock import Mock from ansible.compat.tests.mock import Mock
from ansible.compat.tests.mock import patch from ansible.compat.tests.mock import patch
from ansible.compat.tests.mock import DEFAULT from ansible.compat.tests.mock import DEFAULT
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_snmp_trap import NetworkedParameters from library.bigip_snmp_trap import NetworkedParameters
@ -27,7 +27,8 @@ try:
from library.bigip_snmp_trap import NetworkedManager from library.bigip_snmp_trap import NetworkedManager
from library.bigip_snmp_trap import NonNetworkedManager from library.bigip_snmp_trap import NonNetworkedManager
from library.bigip_snmp_trap import ArgumentSpec from library.bigip_snmp_trap import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
from test.unit.modules.utils import set_module_args from test.unit.modules.utils import set_module_args
except ImportError: except ImportError:
try: try:
@ -37,7 +38,8 @@ except ImportError:
from ansible.modules.network.f5.bigip_snmp_trap import NetworkedManager from ansible.modules.network.f5.bigip_snmp_trap import NetworkedManager
from ansible.modules.network.f5.bigip_snmp_trap import NonNetworkedManager from ansible.modules.network.f5.bigip_snmp_trap import NonNetworkedManager
from ansible.modules.network.f5.bigip_snmp_trap import ArgumentSpec from ansible.modules.network.f5.bigip_snmp_trap import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
except ImportError: except ImportError:
raise SkipTest("F5 Ansible modules require the f5-sdk Python library") raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
@ -77,7 +79,7 @@ class TestParameters(unittest.TestCase):
server='localhost', server='localhost',
user='admin' user='admin'
) )
p = NetworkedParameters(args) p = NetworkedParameters(params=args)
assert p.name == 'foo' assert p.name == 'foo'
assert p.snmp_version == '1' assert p.snmp_version == '1'
assert p.community == 'public' assert p.community == 'public'
@ -97,7 +99,7 @@ class TestParameters(unittest.TestCase):
server='localhost', server='localhost',
user='admin' user='admin'
) )
p = NonNetworkedParameters(args) p = NonNetworkedParameters(params=args)
assert p.name == 'foo' assert p.name == 'foo'
assert p.snmp_version == '1' assert p.snmp_version == '1'
assert p.community == 'public' assert p.community == 'public'
@ -114,7 +116,7 @@ class TestParameters(unittest.TestCase):
version=1, version=1,
port=1000 port=1000
) )
p = NetworkedParameters(args) p = NetworkedParameters(params=args)
assert p.name == 'foo' assert p.name == 'foo'
assert p.snmp_version == '1' assert p.snmp_version == '1'
assert p.community == 'public' assert p.community == 'public'
@ -123,8 +125,6 @@ class TestParameters(unittest.TestCase):
assert p.network == 'other' assert p.network == 'other'
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestManager(unittest.TestCase): class TestManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -143,14 +143,13 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.is_version_non_networked = Mock(return_value=False) mm.is_version_non_networked = Mock(return_value=False)
patches = dict( patches = dict(
@ -178,14 +177,13 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
mm = ModuleManager(client) mm = ModuleManager(module=module)
mm.is_version_non_networked = Mock(return_value=True) mm.is_version_non_networked = Mock(return_value=True)
patches = dict( patches = dict(

@ -17,14 +17,15 @@ if sys.version_info < (2, 7):
from ansible.compat.tests import unittest from ansible.compat.tests import unittest
from ansible.compat.tests.mock import Mock from ansible.compat.tests.mock import Mock
from ansible.compat.tests.mock import patch from ansible.compat.tests.mock import patch
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_software_update import ApiParameters from library.bigip_software_update import ApiParameters
from library.bigip_software_update import ModuleParameters from library.bigip_software_update import ModuleParameters
from library.bigip_software_update import ModuleManager from library.bigip_software_update import ModuleManager
from library.bigip_software_update import ArgumentSpec from library.bigip_software_update import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
from test.unit.modules.utils import set_module_args from test.unit.modules.utils import set_module_args
except ImportError: except ImportError:
try: try:
@ -32,7 +33,8 @@ except ImportError:
from ansible.modules.network.f5.bigip_software_update import ModuleParameters from ansible.modules.network.f5.bigip_software_update import ModuleParameters
from ansible.modules.network.f5.bigip_software_update import ModuleManager from ansible.modules.network.f5.bigip_software_update import ModuleManager
from ansible.modules.network.f5.bigip_software_update import ArgumentSpec from ansible.modules.network.f5.bigip_software_update import ArgumentSpec
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
except ImportError: except ImportError:
raise SkipTest("F5 Ansible modules require the f5-sdk Python library") raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
@ -65,7 +67,7 @@ class TestParameters(unittest.TestCase):
auto_check=True, auto_check=True,
frequency="daily" frequency="daily"
) )
p = ModuleParameters(args) p = ModuleParameters(params=args)
assert p.auto_check == 'enabled' assert p.auto_check == 'enabled'
assert p.frequency == 'daily' assert p.frequency == 'daily'
@ -74,13 +76,11 @@ class TestParameters(unittest.TestCase):
autoCheck="enabled", autoCheck="enabled",
frequency="daily" frequency="daily"
) )
p = ApiParameters(args) p = ApiParameters(params=args)
assert p.auto_check == 'enabled' assert p.auto_check == 'enabled'
assert p.frequency == 'daily' assert p.frequency == 'daily'
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestManager(unittest.TestCase): class TestManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -98,14 +98,13 @@ class TestManager(unittest.TestCase):
# Configure the parameters that would be returned by querying the # Configure the parameters that would be returned by querying the
# remote device # remote device
current = ApiParameters(load_fixture('load_sys_software_update.json')) current = ApiParameters(params=load_fixture('load_sys_software_update.json'))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
mm = ModuleManager(client) mm = ModuleManager(module=module)
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
mm.update_on_device = Mock(return_value=True) mm.update_on_device = Mock(return_value=True)

@ -17,26 +17,26 @@ if sys.version_info < (2, 7):
from ansible.compat.tests import unittest from ansible.compat.tests import unittest
from ansible.compat.tests.mock import Mock from ansible.compat.tests.mock import Mock
from ansible.compat.tests.mock import patch from ansible.compat.tests.mock import patch
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_ssl_certificate import ArgumentSpec from library.bigip_ssl_certificate import ArgumentSpec
from library.bigip_ssl_certificate import KeyParameters from library.bigip_ssl_certificate import ApiParameters
from library.bigip_ssl_certificate import CertParameters from library.bigip_ssl_certificate import ModuleParameters
from library.bigip_ssl_certificate import CertificateManager from library.bigip_ssl_certificate import ModuleManager
from library.bigip_ssl_certificate import HAS_F5SDK from library.bigip_ssl_certificate import HAS_F5SDK
from library.bigip_ssl_certificate import KeyManager from library.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
from test.unit.modules.utils import set_module_args from test.unit.modules.utils import set_module_args
except ImportError: except ImportError:
try: try:
from ansible.modules.network.f5.bigip_ssl_certificate import ArgumentSpec from ansible.modules.network.f5.bigip_ssl_certificate import ArgumentSpec
from ansible.modules.network.f5.bigip_ssl_certificate import KeyParameters from ansible.modules.network.f5.bigip_ssl_certificate import ApiParameters
from ansible.modules.network.f5.bigip_ssl_certificate import CertParameters from ansible.modules.network.f5.bigip_ssl_certificate import ModuleParameters
from ansible.modules.network.f5.bigip_ssl_certificate import CertificateManager from ansible.modules.network.f5.bigip_ssl_certificate import ModuleManager
from ansible.modules.network.f5.bigip_ssl_certificate import HAS_F5SDK from ansible.modules.network.f5.bigip_ssl_certificate import HAS_F5SDK
from ansible.modules.network.f5.bigip_ssl_certificate import KeyManager from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
except ImportError: except ImportError:
raise SkipTest("F5 Ansible modules require the f5-sdk Python library") raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
@ -64,10 +64,10 @@ def load_fixture(name):
class TestParameters(unittest.TestCase): class TestParameters(unittest.TestCase):
def test_module_parameters_key(self): def test_module_parameters_cert(self):
key_content = load_fixture('create_insecure_key1.key') cert_content = load_fixture('create_insecure_cert1.crt')
args = dict( args = dict(
key_content=key_content, content=cert_content,
name="cert1", name="cert1",
partition="Common", partition="Common",
state="present", state="present",
@ -75,45 +75,33 @@ class TestParameters(unittest.TestCase):
server='localhost', server='localhost',
user='admin' user='admin'
) )
p = KeyParameters(args) p = ModuleParameters(params=args)
assert p.name == 'cert1' assert p.name == 'cert1'
assert p.key_filename == 'cert1.key' assert p.filename == 'cert1.crt'
assert '-----BEGIN RSA PRIVATE KEY-----' in p.key_content assert 'Signature Algorithm' in p.content
assert '-----END RSA PRIVATE KEY-----' in p.key_content assert '-----BEGIN CERTIFICATE-----' in p.content
assert p.key_checksum == '91bdddcf0077e2bb2a0258aae2ae3117be392e83' assert '-----END CERTIFICATE-----' in p.content
assert p.checksum == '1e55aa57ee166a380e756b5aa4a835c5849490fe'
assert p.state == 'present' assert p.state == 'present'
assert p.user == 'admin' assert p.user == 'admin'
assert p.server == 'localhost' assert p.server == 'localhost'
assert p.password == 'password' assert p.password == 'password'
assert p.partition == 'Common' assert p.partition == 'Common'
def test_module_parameters_cert(self): def test_module_issuer_cert_key(self):
cert_content = load_fixture('create_insecure_cert1.crt')
args = dict( args = dict(
cert_content=cert_content, issuer_cert='foo',
name="cert1",
partition="Common", partition="Common",
state="present",
password='password',
server='localhost',
user='admin'
) )
p = CertParameters(args) p = ModuleParameters(params=args)
assert p.name == 'cert1' assert p.issuer_cert == '/Common/foo.crt'
assert p.cert_filename == 'cert1.crt'
assert 'Signature Algorithm' in p.cert_content def test_api_issuer_cert_key(self):
assert '-----BEGIN CERTIFICATE-----' in p.cert_content args = load_fixture('load_sys_file_ssl_cert_with_issuer_cert.json')
assert '-----END CERTIFICATE-----' in p.cert_content p = ApiParameters(params=args)
assert p.cert_checksum == '1e55aa57ee166a380e756b5aa4a835c5849490fe' assert p.issuer_cert == '/Common/intermediate.crt'
assert p.state == 'present'
assert p.user == 'admin'
assert p.server == 'localhost'
assert p.password == 'password'
assert p.partition == 'Common'
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestCertificateManager(unittest.TestCase): class TestCertificateManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -122,111 +110,47 @@ class TestCertificateManager(unittest.TestCase):
def test_import_certificate_and_key_no_key_passphrase(self, *args): def test_import_certificate_and_key_no_key_passphrase(self, *args):
set_module_args(dict( set_module_args(dict(
name='foo', name='foo',
cert_content=load_fixture('cert1.crt'), content=load_fixture('cert1.crt'),
key_content=load_fixture('cert1.key'),
state='present', state='present',
password='password', password='password',
server='localhost', server='localhost',
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
# Override methods in the specific type of manager # Override methods in the specific type of manager
cm = CertificateManager(client) mm = ModuleManager(module=module)
cm.exists = Mock(side_effect=[False, True]) mm.exists = Mock(side_effect=[False, True])
cm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)
results = cm.exec_module() results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
def test_import_certificate_chain(self, *args): def test_import_certificate_chain(self, *args):
set_module_args(dict( set_module_args(dict(
name='foo', name='foo',
cert_content=load_fixture('chain1.crt'), content=load_fixture('chain1.crt'),
state='present',
password='password',
server='localhost',
user='admin'
))
client = AnsibleF5Client(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode,
f5_product_name=self.spec.f5_product_name
)
# Override methods in the specific type of manager
cm = CertificateManager(client)
cm.exists = Mock(side_effect=[False, True])
cm.create_on_device = Mock(return_value=True)
results = cm.exec_module()
assert results['changed'] is True
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestKeyManager(unittest.TestCase):
def setUp(self):
self.spec = ArgumentSpec()
def test_import_certificate_and_key_no_key_passphrase(self, *args):
set_module_args(dict(
name='foo',
cert_content=load_fixture('cert1.crt'),
key_content=load_fixture('cert1.key'),
state='present',
password='password',
server='localhost',
user='admin'
))
client = AnsibleF5Client(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode,
f5_product_name=self.spec.f5_product_name
)
# Override methods in the specific type of manager
cm = KeyManager(client)
cm.exists = Mock(side_effect=[False, True])
cm.create_on_device = Mock(return_value=True)
results = cm.exec_module()
assert results['changed'] is True
def test_update_certificate_new_certificate_and_key_password_protected_key(self, *args):
set_module_args(dict(
name='foo',
cert_content=load_fixture('cert2.crt'),
key_content=load_fixture('cert2.key'),
state='present', state='present',
passphrase='keypass',
password='password', password='password',
server='localhost', server='localhost',
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
# Override methods in the specific type of manager # Override methods in the specific type of manager
cm = KeyManager(client) mm = ModuleManager(module=module)
cm.exists = Mock(side_effect=[False, True]) mm.exists = Mock(side_effect=[False, True])
cm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)
results = cm.exec_module() results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True

@ -17,14 +17,15 @@ if sys.version_info < (2, 7):
from ansible.compat.tests import unittest from ansible.compat.tests import unittest
from ansible.compat.tests.mock import Mock from ansible.compat.tests.mock import Mock
from ansible.compat.tests.mock import patch from ansible.compat.tests.mock import patch
from ansible.module_utils.f5_utils import AnsibleF5Client from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_ssl_key import ArgumentSpec from library.bigip_ssl_key import ArgumentSpec
from library.bigip_ssl_key import Parameters from library.bigip_ssl_key import Parameters
from library.bigip_ssl_key import ModuleManager from library.bigip_ssl_key import ModuleManager
from library.bigip_ssl_key import HAS_F5SDK from library.bigip_ssl_key import HAS_F5SDK
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
from test.unit.modules.utils import set_module_args from test.unit.modules.utils import set_module_args
except ImportError: except ImportError:
try: try:
@ -32,7 +33,8 @@ except ImportError:
from ansible.modules.network.f5.bigip_ssl_key import Parameters from ansible.modules.network.f5.bigip_ssl_key import Parameters
from ansible.modules.network.f5.bigip_ssl_key import ModuleManager from ansible.modules.network.f5.bigip_ssl_key import ModuleManager
from ansible.modules.network.f5.bigip_ssl_key import HAS_F5SDK from ansible.modules.network.f5.bigip_ssl_key import HAS_F5SDK
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import F5ModuleError
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
from units.modules.utils import set_module_args from units.modules.utils import set_module_args
except ImportError: except ImportError:
raise SkipTest("F5 Ansible modules require the f5-sdk Python library") raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
@ -71,7 +73,7 @@ class TestParameters(unittest.TestCase):
server='localhost', server='localhost',
user='admin' user='admin'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.name == 'cert1' assert p.name == 'cert1'
assert p.key_filename == 'cert1.key' assert p.key_filename == 'cert1.key'
assert '-----BEGIN RSA PRIVATE KEY-----' in p.content assert '-----BEGIN RSA PRIVATE KEY-----' in p.content
@ -84,8 +86,6 @@ class TestParameters(unittest.TestCase):
assert p.partition == 'Common' assert p.partition == 'Common'
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestModuleManager(unittest.TestCase): class TestModuleManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -101,14 +101,13 @@ class TestModuleManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5Client( module = AnsibleModule(
argument_spec=self.spec.argument_spec, argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode, supports_check_mode=self.spec.supports_check_mode
f5_product_name=self.spec.f5_product_name
) )
# Override methods in the specific type of manager # Override methods in the specific type of manager
cm = ModuleManager(client) cm = ModuleManager(module=module)
cm.exists = Mock(side_effect=[False, True]) cm.exists = Mock(side_effect=[False, True])
cm.create_on_device = Mock(return_value=True) cm.create_on_device = Mock(return_value=True)

Loading…
Cancel
Save