Refactors and deprecation removals (#34830)

This patch is primarily a refactor to make the validate-modules arg-spec
no longer generate a traceback. It additionally includes removal of deprecated
code in the virtual server module.
pull/25294/merge
Tim Rupp 7 years ago committed by GitHub
parent a10aee0fc3
commit c548ab0f18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -30,7 +30,6 @@ options:
that an existing variable is set to C(value). When C(reset) sets the that an existing variable is set to C(value). When C(reset) sets the
variable back to the default value. At least one of value and state variable back to the default value. At least one of value and state
C(reset) are required. C(reset) are required.
required: False
default: present default: present
choices: choices:
- present - present
@ -39,14 +38,9 @@ options:
description: description:
- The value to set the key to. At least one of value and state C(reset) - The value to set the key to. At least one of value and state C(reset)
are required. are required.
required: False
notes: notes:
- Requires the f5-sdk Python package on the host. This is as easy as pip
install f5-sdk.
- Requires BIG-IP version 12.0.0 or greater - Requires BIG-IP version 12.0.0 or greater
extends_documentation_fragment: f5 extends_documentation_fragment: f5
requirements:
- f5-sdk
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
''' '''
@ -98,15 +92,37 @@ value:
sample: false sample: false
''' '''
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:
HAS_F5SDK = 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
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
@ -124,16 +140,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 name(self): def name(self):
return self._values['key'] return self._values['key']
@ -143,12 +149,17 @@ class Parameters(AnsibleF5Parameters):
self._values['key'] = value self._values['key'] = value
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 _update_changed_options(self): def _update_changed_options(self):
changed = {} changed = {}
@ -159,10 +170,10 @@ class ModuleManager(object):
if attr1 != attr2: if attr1 != attr2:
changed[key] = attr1 changed[key] = attr1
if self.want.state == 'reset': if self.want.state == 'reset':
if str(self.want.value) == str(self.want.default_value): if str(self.have.value) != str(self.have.default_value):
changed[self.want.key] = self.want.value changed[self.want.key] = self.have.default_value
if changed: if changed:
self.changes = Parameters(changed) self.changes = Changes(params=changed)
return True return True
return False return False
@ -189,7 +200,7 @@ class ModuleManager(object):
name=self.want.key name=self.want.key
) )
result = resource.attrs result = resource.attrs
return Parameters(result) return Parameters(params=result)
def exists(self): def exists(self):
resource = self.client.api.tm.sys.dbs.db.load( resource = self.client.api.tm.sys.dbs.db.load(
@ -213,7 +224,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
@ -235,9 +246,11 @@ 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.reset_on_device()
self.want.update({'key': self.want.key})
self.want.update({'value': self.have.default_value})
if self.exists(): if self.exists():
return True return True
else: else:
@ -249,13 +262,13 @@ class ModuleManager(object):
resource = self.client.api.tm.sys.dbs.db.load( resource = self.client.api.tm.sys.dbs.db.load(
name=self.want.key name=self.want.key
) )
resource.update(value=self.want.default_value) resource.update(value=self.have.default_value)
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(
key=dict(required=True), key=dict(required=True),
state=dict( state=dict(
default='present', default='present',
@ -263,27 +276,30 @@ class ArgumentSpec(object):
), ),
value=dict() value=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__':

@ -30,48 +30,44 @@ options:
gui_setup: gui_setup:
description: description:
- C(enable) or C(disabled) the Setup utility in the browser-based - C(enable) or C(disabled) the Setup utility in the browser-based
Configuration utility Configuration utility.
choices: ['yes', 'no'] type: bool
lcd_display: lcd_display:
description: description:
- Specifies, when C(enabled), that the system menu displays on the - Specifies, when C(enabled), that the system menu displays on the
LCD screen on the front of the unit. This setting has no effect LCD screen on the front of the unit. This setting has no effect
when used on the VE platform. when used on the VE platform.
choices: ['yes', 'no'] type: bool
mgmt_dhcp: mgmt_dhcp:
description: description:
- Specifies whether or not to enable DHCP client on the management - Specifies whether or not to enable DHCP client on the management
interface interface
choices: ['yes', 'no'] type: bool
net_reboot: net_reboot:
description: description:
- Specifies, when C(enabled), that the next time you reboot the system, - Specifies, when C(enabled), that the next time you reboot the system,
the system boots to an ISO image on the network, rather than an the system boots to an ISO image on the network, rather than an
internal media drive. internal media drive.
choices: ['yes', 'no'] type: bool
quiet_boot: quiet_boot:
description: description:
- Specifies, when C(enabled), that the system suppresses informational - Specifies, when C(enabled), that the system suppresses informational
text on the console during the boot cycle. When C(disabled), the text on the console during the boot cycle. When C(disabled), the
system presents messages and informational text on the console during system presents messages and informational text on the console during
the boot cycle. the boot cycle.
choices: ['yes', 'no'] type: bool
security_banner: security_banner:
description: description:
- Specifies whether the system displays an advisory message on the - Specifies whether the system displays an advisory message on the
login screen. login screen.
choices: ['yes', 'no'] type: bool
state: state:
description: description:
- The state of the variable on the system. When C(present), guarantees - The state of the variable on the system. When C(present), guarantees
that an existing variable is set to C(value). that an existing variable is set to C(value).
required: false
default: present default: present
choices: choices:
- present - present
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: requirements:
- f5-sdk - f5-sdk
@ -139,20 +135,40 @@ security_banner:
sample: enabled sample: enabled
''' '''
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
from ansible.module_utils.f5_utils import F5ModuleError
from ansible.module_utils.parsing.convert_bool import BOOLEANS from ansible.module_utils.parsing.convert_bool import BOOLEANS
from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE
from ansible.module_utils.parsing.convert_bool import BOOLEANS_FALSE from ansible.module_utils.parsing.convert_bool import BOOLEANS_FALSE
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:
HAS_F5SDK = 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
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
@ -182,46 +198,6 @@ class Parameters(AnsibleF5Parameters):
'mgmt_dhcp', 'net_reboot', 'quiet_boot', 'console_timeout' 'mgmt_dhcp', 'net_reboot', 'quiet_boot', 'console_timeout'
] ]
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):
pass pass
@ -323,9 +299,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()
@ -335,7 +312,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)
@ -351,7 +328,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
@ -369,7 +346,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))
@ -379,7 +356,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']
) )
@ -390,13 +367,13 @@ class ModuleManager(object):
def read_current_from_device(self): def read_current_from_device(self):
resource = self.client.api.tm.sys.global_settings.load() resource = self.client.api.tm.sys.global_settings.load()
result = resource.attrs result = resource.attrs
return ApiParameters(result) return ApiParameters(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
@ -412,7 +389,7 @@ class ArgumentSpec(object):
self.supports_check_mode = True self.supports_check_mode = True
self.states = ['present'] self.states = ['present']
self.on_off_choices = ['enabled', 'disabled', 'True', 'False'] + list(BOOLEANS) self.on_off_choices = ['enabled', 'disabled', 'True', 'False'] + list(BOOLEANS)
self.argument_spec = dict( argument_spec = dict(
security_banner=dict( security_banner=dict(
choices=self.on_off_choices choices=self.on_off_choices
), ),
@ -435,39 +412,30 @@ class ArgumentSpec(object):
console_timeout=dict(required=False, type='int', default=None), console_timeout=dict(required=False, type='int', default=None),
state=dict(default='present', choices=['present']) state=dict(default='present', choices=['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")
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__':

@ -24,11 +24,20 @@ options:
description: description:
- The name of the traffic group. - The name of the traffic group.
required: True required: True
notes: partition:
- Requires the f5-sdk Python package on the host. This is as easy as description:
C(pip install f5-sdk). - Device partition to manage resources on.
requirements: default: Common
- f5-sdk >= 3.0.5 version_added: 2.5
state:
description:
- When C(present), ensures that the traffic group exists.
- When C(absent), ensures the traffic group is removed.
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)
@ -49,18 +58,38 @@ RETURN = r'''
# only common fields returned # only common fields returned
''' '''
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
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.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:
HAS_F5SDK = 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
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
@ -80,36 +109,6 @@ class Parameters(AnsibleF5Parameters):
] ]
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 = {}
try: try:
@ -120,16 +119,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
class Changes(Parameters): class Changes(Parameters):
pass pass
@ -164,9 +153,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 = Parameters(self.client.module.params) self.client = kwargs.get('client', None)
self.want = Parameters(params=self.module.params)
self.changes = Changes() self.changes = Changes()
def _set_changed_options(self): def _set_changed_options(self):
@ -175,38 +165,9 @@ 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)
def _update_changed_options(self): def _update_changed_options(self):
"""Sets the changed updatables when updating a resource
A module needs to know what changed to determine whether to update
a resource (or set of resources). This method accomplishes this by
invoking the Difference engine code.
Each parameter in the `Parameter` class' `updatables` array will be
given to the Difference engine's `compare` method. This is done in the
order the updatables are listed in the array.
The `compare` method updates the `changes` dictionary if the following
way,
* If `None` is returned, a change will not be registered.
* If a dictionary is returned, the `changes` dictionary will be updated
with the values in what was returned.
* Otherwise, the `changes` dictionary's key (the parameter being
compared) will be set to the value that is returned by `compare`
The dictionary behavior is in place to allow you to change the key
that is set in the `changes` dictionary. There are frequently cases
where there is not a clean API map that can be set, nor a way to
otherwise allow you to change the attribute name of the resource being
updated before it is sent off to the remote device. Using a dictionary
return value of `compare` allows you to do this.
Returns:
bool: True when changes are present. False otherwise.
"""
diff = Difference(self.want, self.have) diff = Difference(self.want, self.have)
updatables = Parameters.updatables updatables = Parameters.updatables
changed = dict() changed = dict()
@ -220,7 +181,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)
return True return True
return False return False
@ -252,7 +213,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']
) )
@ -274,13 +235,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():
@ -293,7 +254,7 @@ class ModuleManager(object):
raise F5ModuleError( raise F5ModuleError(
"Traffic groups can only be created in the /Common partition" "Traffic groups can only be created in the /Common partition"
) )
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
@ -333,49 +294,44 @@ class ModuleManager(object):
partition=self.want.partition partition=self.want.partition
) )
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.argument_spec = dict( argument_spec = dict(
name=dict(required=True), name=dict(required=True),
state=dict(default='present', choices=['absent', 'present']) state=dict(default='present', choices=['absent', 'present']),
) partition=dict(
self.f5_product_name = 'bigip' default='Common',
fallback=(env_fallback, ['F5_PARTITION'])
)
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__':

@ -87,8 +87,6 @@ options:
- installed - installed
- present - present
notes: notes:
- Requires the f5-sdk Python package on the host. This is as easy as
pip install f5-sdk.
- Only the most basic checks are performed by this module. Other checks and - Only the most basic checks are performed by this module. Other checks and
considerations need to be taken into account. See the following URL. considerations need to be taken into account. See the following URL.
https://support.f5.com/kb/en-us/solutions/public/11000/300/sol11318.html https://support.f5.com/kb/en-us/solutions/public/11000/300/sol11318.html
@ -110,8 +108,6 @@ notes:
- This module does not support restoring encrypted archives on replacement - This module does not support restoring encrypted archives on replacement
RMA units. RMA units.
extends_documentation_fragment: f5 extends_documentation_fragment: f5
requirements:
- f5-sdk
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
''' '''
@ -182,28 +178,49 @@ RETURN = r'''
import os import os
import re import re
import sys
import time import time
from distutils.version import LooseVersion from ansible.module_utils.basic import AnsibleModule
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 ansible.module_utils.six import iteritems from ansible.module_utils.six import iteritems
from distutils.version import LooseVersion
HAS_DEVEL_IMPORTS = False
try: try:
from collections import OrderedDict # 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: try:
from ordereddict import OrderedDict from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError: except ImportError:
pass HAS_F5SDK = False
try: try:
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError from collections import OrderedDict
except ImportError: except ImportError:
HAS_F5SDK = False try:
from ordereddict import OrderedDict
except ImportError:
pass
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
@ -292,14 +309,15 @@ class Parameters(AnsibleF5Parameters):
class ModuleManager(object): class ModuleManager(object):
def __init__(self, client): def __init__(self, *args, **kwargs):
self.client = client self.client = kwargs.get('client', None)
self.kwargs = kwargs
def exec_module(self): def exec_module(self):
if self.is_version_v1(): if self.is_version_v1():
manager = V1Manager(self.client) manager = V1Manager(**self.kwargs)
else: else:
manager = V2Manager(self.client) manager = V2Manager(**self.kwargs)
return manager.exec_module() return manager.exec_module()
@ -321,10 +339,10 @@ 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.have = None self.client = kwargs.get('client', None)
self.want = Parameters(self.client.module.params) self.want = Parameters(params=self.module.params)
self.changes = Parameters() self.changes = Parameters()
def exec_module(self): def exec_module(self):
@ -352,7 +370,7 @@ class BaseManager(object):
return self.create() return self.create()
def update(self): def update(self):
if self.client.check_mode: if self.module.check_mode:
if self.want.force: if self.want.force:
return True return True
return False return False
@ -365,7 +383,7 @@ class BaseManager(object):
return False return False
def create(self): def create(self):
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():
@ -386,7 +404,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():
@ -466,7 +484,6 @@ class V1Manager(BaseManager):
* No API to upload UCS files * No API to upload UCS files
""" """
def create_on_device(self): def create_on_device(self):
remote_path = "/var/local/ucs" remote_path = "/var/local/ucs"
tpath_name = '/var/config/rest/downloads' tpath_name = '/var/config/rest/downloads'
@ -563,7 +580,7 @@ class V2Manager(V1Manager):
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(
force=dict( force=dict(
type='bool', type='bool',
default='no' default='no'
@ -585,30 +602,30 @@ class ArgumentSpec(object):
), ),
ucs=dict(required=True) ucs=dict(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")
if sys.version_info < (2, 7):
raise F5ModuleError("F5 Ansible modules require Python >= 2.7")
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__':

@ -74,13 +74,14 @@ options:
choices: choices:
- always - always
- on_create - on_create
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.
- Requires BIG-IP versions >= 12.0.0 - Requires BIG-IP versions >= 12.0.0
extends_documentation_fragment: f5 extends_documentation_fragment: f5
requirements:
- f5-sdk
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
- Wojciech Wypior (@wojtek0806) - Wojciech Wypior (@wojtek0806)
@ -191,23 +192,44 @@ shell:
import re import re
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback
from distutils.version import LooseVersion from distutils.version import LooseVersion
from ansible.module_utils.f5_utils import AnsibleF5Client
from ansible.module_utils.f5_utils import AnsibleF5Parameters HAS_DEVEL_IMPORTS = False
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 collections import defaultdict
try: try:
from StringIO import StringIO # 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:
from io import StringIO # 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 StringIO import StringIO
except ImportError: except ImportError:
HAS_F5SDK = False from io import StringIO
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
@ -228,36 +250,6 @@ class Parameters(AnsibleF5Parameters):
'shell', 'partitionAccess', 'description', 'name', 'password' 'shell', 'partitionAccess', 'description', 'name', 'password'
] ]
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
@property @property
def partition_access(self): def partition_access(self):
"""Partition access values will require some transformation. """Partition access values will require some transformation.
@ -324,18 +316,28 @@ 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_root_username_credential(): if self.is_root_username_credential():
manager = RootUserManager(self.client) manager = self.get_manager('root')
elif self.is_version_less_than_13(): elif self.is_version_less_than_13():
manager = UnparitionedManager(self.client) manager = self.get_manager('v1')
else: else:
manager = PartitionedManager(self.client) manager = self.get_manager('v2')
return manager.exec_module() return manager.exec_module()
def get_manager(self, type):
if type == 'root':
return RootUserManager(**self.kwargs)
elif type == 'v1':
return UnparitionedManager(**self.kwargs)
elif type == 'v2':
return PartitionedManager(**self.kwargs)
def is_version_less_than_13(self): def is_version_less_than_13(self):
"""Checks to see if the TMOS version is less than 13 """Checks to see if the TMOS version is less than 13
@ -351,17 +353,18 @@ class ModuleManager(object):
return False return False
def is_root_username_credential(self): def is_root_username_credential(self):
user = self.client.module.params.get('username_credential', None) user = self.module.params.get('username_credential', None)
if user == 'root': if user == 'root':
return True return True
return False return False
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
self.want = Parameters(self.client.module.params) self.want = Parameters(params=self.module.params)
self.changes = Parameters() self.changes = Parameters()
def exec_module(self): def exec_module(self):
@ -388,7 +391,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 _update_changed_options(self): def _update_changed_options(self):
changed = {} changed = {}
@ -411,7 +414,7 @@ class BaseManager(object):
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
@ -481,13 +484,13 @@ 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
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():
@ -499,7 +502,7 @@ class BaseManager(object):
if self.want.shell == 'bash': if self.want.shell == 'bash':
self.validate_shell_parameter() self.validate_shell_parameter()
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
@ -518,7 +521,7 @@ class UnparitionedManager(BaseManager):
def read_current_from_device(self): def read_current_from_device(self):
tmp_res = self.client.api.tm.auth.users.user.load(name=self.want.name) tmp_res = self.client.api.tm.auth.users.user.load(name=self.want.name)
result = tmp_res.attrs result = tmp_res.attrs
return Parameters(result) return Parameters(params=result)
def exists(self): def exists(self):
return self.client.api.tm.auth.users.user.exists(name=self.want.name) return self.client.api.tm.auth.users.user.exists(name=self.want.name)
@ -570,7 +573,7 @@ class PartitionedManager(BaseManager):
def read_current_from_device(self): def read_current_from_device(self):
resource = self._read_one_resource_from_collection() resource = self._read_one_resource_from_collection()
result = resource.attrs result = resource.attrs
return Parameters(result) return Parameters(params=result)
def exists(self): def exists(self):
collection = self.client.api.tm.auth.users.get_collection( collection = self.client.api.tm.auth.users.get_collection(
@ -646,7 +649,7 @@ class RootUserManager(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(
name=dict( name=dict(
required=True, required=True,
aliases=['username_credential'] aliases=['username_credential']
@ -664,41 +667,37 @@ class ArgumentSpec(object):
update_password=dict( update_password=dict(
default='always', default='always',
choices=['always', 'on_create'] choices=['always', 'on_create']
),
state=dict(default='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 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__':

@ -112,9 +112,11 @@ options:
- The number you can specify depends on the type of hardware you have. - The number you can specify depends on the type of hardware you have.
- In the event of a reboot, the system persists the guest to the same slot on - In the event of a reboot, the system persists the guest to the same slot on
which it ran prior to the reboot. which it ran prior to the reboot.
partition:
description:
- Device partition to manage resources on.
default: Common
notes: notes:
- Requires the f5-sdk Python package on the host. This is as easy as pip
install f5-sdk.
- This module can take a lot of time to deploy vCMP guests. This is an intrinsic - This module can take a lot of time to deploy vCMP guests. This is an intrinsic
limitation of the vCMP system because it is booting real VMs on the BIG-IP limitation of the vCMP system because it is booting real VMs on the BIG-IP
device. This boot time is very similar in length to the time it takes to device. This boot time is very similar in length to the time it takes to
@ -123,8 +125,6 @@ notes:
means that it is not unusual for a vCMP host with many guests to take a means that it is not unusual for a vCMP host with many guests to take a
long time (60+ minutes) to reboot and bring all the guests online. The long time (60+ minutes) to reboot and bring all the guests online. The
BIG-IP chassis will be available before all vCMP guests are online. BIG-IP chassis will be available before all vCMP guests are online.
requirements:
- f5-sdk >= 3.0.3
- netaddr - netaddr
extends_documentation_fragment: f5 extends_documentation_fragment: f5
author: author:
@ -173,16 +173,41 @@ vlans:
sample: ['/Common/vlan1', '/Common/vlan2'] sample: ['/Common/vlan1', '/Common/vlan2']
''' '''
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.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 collections import defaultdict
from collections import namedtuple from collections import namedtuple
import time HAS_DEVEL_IMPORTS = False
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 netaddr import IPAddress, AddrFormatError, IPNetwork from netaddr import IPAddress, AddrFormatError, IPNetwork
@ -192,7 +217,6 @@ except ImportError:
try: try:
from f5.utils.responses.handlers import Stats from f5.utils.responses.handlers import Stats
from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError
except ImportError: except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
@ -222,37 +246,6 @@ class Parameters(AnsibleF5Parameters):
'state' 'state'
] ]
def __init__(self, params=None, client=None):
self._values = defaultdict(lambda: None)
self._values['__warnings'] = []
if params:
self.update(params=params)
self.client = client
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)
@ -268,16 +261,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
@property @property
def mgmt_route(self): def mgmt_route(self):
if self._values['mgmt_route'] is None: if self._values['mgmt_route'] is None:
@ -394,9 +377,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 = Parameters(client=client, params=self.client.module.params) self.client = kwargs.get('client', None)
self.want = Parameters(client=self.client, params=self.module.params)
self.changes = Changes() self.changes = Changes()
def _set_changed_options(self): def _set_changed_options(self):
@ -405,7 +389,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)
def _update_changed_options(self): def _update_changed_options(self):
diff = Difference(self.want, self.have) diff = Difference(self.want, self.have)
@ -418,7 +402,7 @@ class ModuleManager(object):
else: else:
changed[k] = change changed[k] = change
if changed: if changed:
self.changes = Parameters(changed) self.changes = Parameters(params=changed)
return True return True
return False return False
@ -450,7 +434,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']
) )
@ -476,7 +460,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()
if self.want.state == 'provisioned': if self.want.state == 'provisioned':
@ -488,7 +472,7 @@ class ModuleManager(object):
return True return True
def remove(self): def remove(self):
if self.client.check_mode: if self.module.check_mode:
return True return True
if self.want.delete_virtual_disk: if self.want.delete_virtual_disk:
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
@ -501,7 +485,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
if self.want.mgmt_tuple.subnet is None: if self.want.mgmt_tuple.subnet is None:
self.want.update(dict( self.want.update(dict(
@ -547,7 +531,7 @@ class ModuleManager(object):
name=self.want.name name=self.want.name
) )
result = resource.attrs result = resource.attrs
return Parameters(result) return Parameters(params=result)
def remove_virtual_disk(self): def remove_virtual_disk(self):
if self.virtual_disk_exists(): if self.virtual_disk_exists():
@ -666,7 +650,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),
vlans=dict(type='list'), vlans=dict(type='list'),
mgmt_network=dict(choices=['bridged', 'isolated', 'host only']), mgmt_network=dict(choices=['bridged', 'isolated', 'host only']),
@ -680,47 +664,41 @@ class ArgumentSpec(object):
delete_virtual_disk=dict( delete_virtual_disk=dict(
type='bool', default='no' type='bool', default='no'
), ),
cores_per_slot=dict(type='int') cores_per_slot=dict(type='int'),
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)
self.required_if = [ self.required_if = [
['mgmt_network', 'bridged', ['mgmt_address']] ['mgmt_network', 'bridged', ['mgmt_address']]
] ]
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__':

@ -100,14 +100,11 @@ options:
- Specifies whether the system uses route advertisement for this - Specifies whether the system uses route advertisement for this
virtual address. When disabled, the system does not advertise virtual address. When disabled, the system does not advertise
routes for this virtual address. routes for this virtual address.
choices: type: bool
- yes
- no
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 version_added: 2.5
traffic_group: traffic_group:
description: description:
@ -116,13 +113,10 @@ options:
will be used. will be used.
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 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
- netaddr - netaddr
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
@ -193,23 +187,46 @@ state:
sample: disabled sample: disabled
''' '''
try: from ansible.module_utils.basic import AnsibleModule
import netaddr from ansible.module_utils.basic import env_fallback
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 HAS_F5SDK
from ansible.module_utils.f5_utils import F5ModuleError
from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE
from ansible.module_utils.parsing.convert_bool import BOOLEANS_FALSE from ansible.module_utils.parsing.convert_bool import BOOLEANS_FALSE
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:
HAS_F5SDK = 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:
import netaddr
HAS_NETADDR = True
except ImportError:
HAS_NETADDR = False
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
@ -349,17 +366,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 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 Changes(Parameters): class Changes(Parameters):
pass pass
@ -393,10 +399,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(client=self.client, params=self.module.params)
self.changes = Changes() self.changes = Changes()
def _set_changed_options(self): def _set_changed_options(self):
@ -405,7 +412,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)
def _update_changed_options(self): def _update_changed_options(self):
diff = Difference(self.want, self.have) diff = Difference(self.want, self.have)
@ -421,7 +428,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)
return True return True
return False return False
@ -467,7 +474,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.virtual_address_s.virtual_address.exists( result = self.client.api.tm.ltm.virtual_address_s.virtual_address.exists(
@ -481,18 +488,18 @@ class ModuleManager(object):
if self.want.netmask is not None: if self.want.netmask is not None:
if self.have.netmask != self.want.netmask: if self.have.netmask != self.want.netmask:
raise F5ModuleError( raise F5ModuleError(
"The netmask cannot be changed. Delete and recreate" "The netmask cannot be changed. Delete and recreate "
"the virtual address if you need to do this." "the virtual address if you need to do this."
) )
if self.want.address is not None: if self.want.address is not None:
if self.have.address != self.want.address: if self.have.address != self.want.address:
raise F5ModuleError( raise F5ModuleError(
"The address cannot be changed. Delete and recreate" "The address cannot be changed. Delete and recreate "
"the virtual address if you need to do this." "the virtual address if you need to do this."
) )
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
@ -509,7 +516,7 @@ class ModuleManager(object):
self._set_changed_options() self._set_changed_options()
if self.want.traffic_group is None: if self.want.traffic_group is None:
self.want.update({'traffic_group': '/Common/traffic-group-1'}) self.want.update({'traffic_group': '/Common/traffic-group-1'})
if self.client.check_mode: if self.module.check_mode:
return True return True
self.create_on_device() self.create_on_device()
if self.exists(): if self.exists():
@ -527,7 +534,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():
@ -545,7 +552,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(
state=dict( state=dict(
default='present', default='present',
choices=['present', 'absent', 'disabled', 'enabled'] choices=['present', 'absent', 'disabled', 'enabled']
@ -577,29 +584,38 @@ class ArgumentSpec(object):
use_route_advertisement=dict( use_route_advertisement=dict(
type='bool' type='bool'
), ),
traffic_group=dict() traffic_group=dict(),
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")
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__':

@ -137,14 +137,6 @@ options:
- Default Profile which manages the session persistence. - Default Profile which manages the session persistence.
- If you want to remove the existing default persistence profile, specify an - If you want to remove the existing default persistence profile, specify an
empty value; C(""). See the documentation for an example. empty value; C(""). See the documentation for an example.
route_advertisement_state:
description:
- Enable route advertisement for destination.
- Deprecated in 2.4. Use the C(bigip_virtual_address) module instead.
choices:
- enabled
- disabled
version_added: "2.3"
description: description:
description: description:
- Virtual server description. - Virtual server description.
@ -171,12 +163,9 @@ options:
version_added: 2.5 version_added: 2.5
notes: notes:
- Requires BIG-IP software version >= 11 - Requires BIG-IP software version >= 11
- 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.
requirements: requirements:
- f5-sdk
- netaddr - netaddr
extends_documentation_fragment: f5 extends_documentation_fragment: f5
author: author:
@ -381,18 +370,38 @@ 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
from collections import namedtuple from collections import namedtuple
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:
HAS_F5SDK = 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:
import netaddr import netaddr
@ -402,100 +411,6 @@ except ImportError:
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
def __init__(self, params=None):
self._values = defaultdict(lambda: None)
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):
result = {}
for returnable in self.returnables:
try:
result[returnable] = getattr(self, returnable)
except Exception as ex:
pass
result = self._filter_params(result)
return result
def _fqdn_name(self, value):
if value is not None and not value.startswith('/'):
return '/{0}/{1}'.format(self.partition, 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 VirtualAddressParameters(Parameters):
api_map = {
'routeAdvertisement': 'route_advertisement_state'
}
returnables = [
'route_advertisement_state'
]
updatables = [
'route_advertisement_state'
]
api_attributes = [
'routeAdvertisement'
]
class VirtualAddressModuleParameters(VirtualAddressParameters):
@property
def route_advertisement_state(self):
# TODO: Remove in 2.5
if self._values['route_advertisement_state'] is None:
return None
if self._values['__warnings'] is None:
self._values['__warnings'] = []
self._values['__warnings'].append(
dict(
msg="Usage of the 'route_advertisement_state' parameter is deprecated. Use the bigip_virtual_address module instead",
version='2.4'
)
)
return str(self._values['route_advertisement_state'])
class VirtualAddressApiParameters(VirtualAddressParameters):
pass
class VirtualServerParameters(Parameters):
api_map = { api_map = {
'sourceAddressTranslation': 'snat', 'sourceAddressTranslation': 'snat',
'fallbackPersistence': 'fallback_persistence_profile', 'fallbackPersistence': 'fallback_persistence_profile',
@ -566,12 +481,25 @@ class VirtualServerParameters(Parameters):
'vlans_disabled' 'vlans_disabled'
] ]
def __init__(self, params=None): profiles_mutex = [
super(VirtualServerParameters, self).__init__(params) 'sip', 'sipsession', 'iiop', 'rtsp', 'http', 'diameter',
self.profiles_mutex = [ 'diametersession', 'radius', 'ftp', 'tftp', 'dns', 'pptp', 'fix'
'sip', 'sipsession', 'iiop', 'rtsp', 'http', 'diameter', ]
'diametersession', 'radius', 'ftp', 'tftp', 'dns', 'pptp', 'fix'
] def to_return(self):
result = {}
for returnable in self.returnables:
try:
result[returnable] = getattr(self, returnable)
except Exception as ex:
pass
result = self._filter_params(result)
return result
def _fqdn_name(self, value):
if value is not None and not value.startswith('/'):
return '/{0}/{1}'.format(self.partition, value)
return value
def is_valid_ip(self, value): def is_valid_ip(self, value):
try: try:
@ -618,7 +546,7 @@ class VirtualServerParameters(Parameters):
return result return result
class VirtualServerApiParameters(VirtualServerParameters): class ApiParameters(Parameters):
@property @property
def destination(self): def destination(self):
if self._values['destination'] is None: if self._values['destination'] is None:
@ -648,7 +576,7 @@ class VirtualServerApiParameters(VirtualServerParameters):
if self._values['destination'] is None: if self._values['destination'] is None:
result = Destination(ip=None, port=None, route_domain=None) result = Destination(ip=None, port=None, route_domain=None)
return result return result
destination = re.sub(r'^/[a-zA-Z_.-]+/', '', self._values['destination']) destination = re.sub(r'^/[a-zA-Z0-9_.-]+/', '', self._values['destination'])
if self.is_valid_ip(destination): if self.is_valid_ip(destination):
result = Destination( result = Destination(
@ -816,7 +744,7 @@ class VirtualServerApiParameters(VirtualServerParameters):
return result return result
class VirtualServerModuleParameters(VirtualServerParameters): class ModuleParameters(Parameters):
def _handle_profile_context(self, tmp): def _handle_profile_context(self, tmp):
if 'context' not in tmp: if 'context' not in tmp:
tmp['context'] = 'all' tmp['context'] = 'all'
@ -1106,7 +1034,11 @@ class VirtualServerModuleParameters(VirtualServerParameters):
return result return result
class VirtualServerUsableChanges(VirtualServerParameters): class Changes(Parameters):
pass
class UsableChanges(Changes):
@property @property
def vlans(self): def vlans(self):
if self._values['vlans'] is None: if self._values['vlans'] is None:
@ -1118,11 +1050,7 @@ class VirtualServerUsableChanges(VirtualServerParameters):
return self._values['vlans'] return self._values['vlans']
class VirtualAddressUsableChanges(VirtualAddressParameters): class ReportableChanges(Changes):
pass
class VirtualServerReportableChanges(VirtualServerParameters):
@property @property
def snat(self): def snat(self):
if self._values['snat'] is None: if self._values['snat'] is None:
@ -1137,13 +1065,13 @@ class VirtualServerReportableChanges(VirtualServerParameters):
@property @property
def destination(self): def destination(self):
params = VirtualServerApiParameters(dict(destination=self._values['destination'])) params = ApiParameters(params=dict(destination=self._values['destination']))
result = params.destination_tuple.ip result = params.destination_tuple.ip
return result return result
@property @property
def port(self): def port(self):
params = VirtualServerApiParameters(dict(destination=self._values['destination'])) params = ApiParameters(params=dict(destination=self._values['destination']))
result = params.destination_tuple.port result = params.destination_tuple.port
return result return result
@ -1175,10 +1103,6 @@ class VirtualServerReportableChanges(VirtualServerParameters):
return self._values['vlans'] return self._values['vlans']
class VirtualAddressReportableChanges(VirtualAddressParameters):
pass
class Difference(object): class Difference(object):
def __init__(self, want, have=None): def __init__(self, want, have=None):
self.have = have self.have = have
@ -1461,49 +1385,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)
def exec_module(self): self.have = ApiParameters()
managers = list() self.want = ModuleParameters(client=self.client, params=self.module.params)
managers.append(self.get_manager('virtual_server')) self.changes = UsableChanges()
if self.client.module.params['route_advertisement_state'] is not None:
managers.append(self.get_manager('virtual_address'))
result = self.execute_managers(managers)
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):
vsm = VirtualServerManager(self.client)
if type == 'virtual_server':
return vsm
elif type == 'virtual_address':
self.set_name_of_virtual_address()
result = VirtualAddressManager(self.client)
return result
def set_name_of_virtual_address(self):
mgr = VirtualServerManager(self.client)
params = mgr.read_current_from_device()
destination = params.destination_tuple
self.client.module.params['name'] = destination.ip
class BaseManager(object):
def __init__(self, client):
self.client = client
self.have = None
def exec_module(self): def exec_module(self):
changed = False changed = False
@ -1518,7 +1405,7 @@ class BaseManager(object):
except iControlUnexpectedHTTPError as e: except iControlUnexpectedHTTPError as e:
raise F5ModuleError(str(e)) raise F5ModuleError(str(e))
reportable = self.get_reportable_changes() 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))
@ -1528,7 +1415,7 @@ class BaseManager(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']
) )
@ -1548,23 +1435,11 @@ 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
def create(self):
if self.client.check_mode:
return True
# This must be changed back to a list to make a valid REST API
# value. The module manipulates this as a normal dictionary
if self.want.default_persistence_profile is not None:
self.want.update({'default_persistence_profile': [self.want.default_persistence_profile]})
self.create_on_device()
return True
def should_update(self): def should_update(self):
result = self._update_changed_options() result = self._update_changed_options()
if result: if result:
@ -1572,36 +1447,28 @@ 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():
raise F5ModuleError("Failed to delete the resource") raise F5ModuleError("Failed to delete the resource")
return True return True
class VirtualServerManager(BaseManager):
def __init__(self, client):
super(VirtualServerManager, self).__init__(client)
self.have = None
self.want = VirtualServerModuleParameters(self.client.module.params)
self.changes = VirtualServerUsableChanges()
def get_reportable_changes(self): def get_reportable_changes(self):
result = VirtualServerReportableChanges(self.changes.to_return()) result = ReportableChanges(params=self.changes.to_return())
return result return result
def _set_changed_options(self): def _set_changed_options(self):
changed = {} changed = {}
for key in VirtualServerParameters.returnables: for key in Parameters.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 = VirtualServerUsableChanges(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)
updatables = VirtualServerParameters.updatables updatables = Parameters.updatables
changed = dict() changed = dict()
for k in updatables: for k in updatables:
change = diff.compare(k) change = diff.compare(k)
@ -1613,7 +1480,7 @@ class VirtualServerManager(BaseManager):
else: else:
changed[k] = change changed[k] = change
if changed: if changed:
self.changes = VirtualServerUsableChanges(changed) self.changes = UsableChanges(params=changed)
return True return True
return False return False
@ -1628,6 +1495,11 @@ class VirtualServerManager(BaseManager):
required_resources = ['destination', 'port'] required_resources = ['destination', 'port']
self._set_changed_options() self._set_changed_options()
# This must be changed back to a list to make a valid REST API
# value. The module manipulates this as a normal dictionary
if self.want.default_persistence_profile is not None:
self.want.update({'default_persistence_profile': [self.want.default_persistence_profile]})
if self.want.destination is None: if self.want.destination is None:
raise F5ModuleError( raise F5ModuleError(
"'destination' must be specified when creating a virtual server" "'destination' must be specified when creating a virtual server"
@ -1652,7 +1524,10 @@ class VirtualServerManager(BaseManager):
raise F5ModuleError( raise F5ModuleError(
"The source and destination addresses for the virtual server must be be the same type (IPv4 or IPv6)." "The source and destination addresses for the virtual server must be be the same type (IPv4 or IPv6)."
) )
return super(VirtualServerManager, self).create() if self.module.check_mode:
return True
self.create_on_device()
return True
def update_on_device(self): def update_on_device(self):
params = self.changes.api_params() params = self.changes.api_params()
@ -1674,7 +1549,7 @@ class VirtualServerManager(BaseManager):
) )
params = result.attrs params = result.attrs
params.update(dict(kind=result.to_dict().get('kind', None))) params.update(dict(kind=result.to_dict().get('kind', None)))
result = VirtualServerApiParameters(params) result = ApiParameters(params=params)
return result return result
def create_on_device(self): def create_on_device(self):
@ -1694,71 +1569,10 @@ class VirtualServerManager(BaseManager):
resource.delete() resource.delete()
class VirtualAddressManager(BaseManager):
def __init__(self, client):
super(VirtualAddressManager, self).__init__(client)
self.want = VirtualAddressModuleParameters(self.client.module.params)
self.have = VirtualAddressApiParameters()
self.changes = VirtualAddressUsableChanges()
def get_reportable_changes(self):
result = VirtualAddressReportableChanges(self.changes.to_return())
return result
def _set_changed_options(self):
changed = {}
for key in VirtualAddressParameters.returnables:
if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key)
if changed:
self.changes = VirtualAddressUsableChanges(changed)
def _update_changed_options(self):
diff = Difference(self.want, self.have)
updatables = VirtualAddressParameters.updatables
changed = dict()
for k in updatables:
change = diff.compare(k)
if change is None:
continue
else:
if isinstance(change, dict):
changed.update(change)
else:
changed[k] = change
if changed:
self.changes = VirtualAddressUsableChanges(changed)
return True
return False
def read_current_from_device(self):
result = self.client.api.tm.ltm.virtual_address_s.virtual_address.load(
name=self.want.name,
partition=self.want.partition
)
result = VirtualAddressParameters(result.attrs)
return result
def update_on_device(self):
params = self.want.api_params()
resource = self.client.api.tm.ltm.virtual_address_s.virtual_address.load(
name=self.want.name,
partition=self.want.partition
)
resource.modify(**params)
def exists(self):
result = self.client.api.tm.ltm.virtual_address_s.virtual_address.exists(
name=self.want.name,
partition=self.want.partition
)
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(
state=dict( state=dict(
default='present', default='present',
choices=['present', 'absent', 'disabled', 'enabled'] choices=['present', 'absent', 'disabled', 'enabled']
@ -1798,54 +1612,45 @@ class ArgumentSpec(object):
pool=dict(), pool=dict(),
description=dict(), description=dict(),
snat=dict(), snat=dict(),
route_advertisement_state=dict(
choices=['enabled', 'disabled']
),
default_persistence_profile=dict(), default_persistence_profile=dict(),
fallback_persistence_profile=dict(), fallback_persistence_profile=dict(),
source=dict(), source=dict(),
metadata=dict(type='raw') metadata=dict(type='raw'),
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)
self.mutually_exclusive = [ self.mutually_exclusive = [
['enabled_vlans', 'disabled_vlans'] ['enabled_vlans', 'disabled_vlans']
] ]
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,
mutually_exclusive=spec.mutually_exclusive mutually_exclusive=spec.mutually_exclusive
) )
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__':

@ -55,13 +55,46 @@ options:
- Tag number for the VLAN. The tag number can be any integer between 1 - Tag number for the VLAN. The tag number can be any integer between 1
and 4094. The system automatically assigns a tag number if you do not and 4094. The system automatically assigns a tag number if you do not
specify a value. specify a value.
mtu:
description:
- Specifies the maximum transmission unit (MTU) for traffic on this VLAN.
When creating a new VLAN, if this parameter is not specified, the default
value used will be C(1500).
- This number must be between 576 to 9198.
version_added: 2.5
cmp_hash:
description:
- Specifies how the traffic on the VLAN will be disaggregated. The value
selected determines the traffic disaggregation method. You can choose to
disaggregate traffic based on C(source-address) (the source IP address),
C(destination-address) (destination IP address), or C(default), which
specifies that the default CMP hash uses L4 ports.
- When creating a new VLAN, if this parameter is not specified, the default
of C(default) is used.
version_added: 2.5
dag_tunnel:
description:
- Specifies how the disaggregator (DAG) distributes received tunnel-encapsulated
packets to TMM instances. Select C(inner) to distribute packets based on information
in inner headers. Select C(outer) to distribute packets based on information in
outer headers without inspecting inner headers.
- When creating a new VLAN, if this parameter is not specified, the default
of C(outer) is used.
- This parameter is not supported on Virtual Editions of BIG-IP.
version_added: 2.5
dag_round_robin:
description:
- Specifies whether some of the stateless traffic on the VLAN should be
disaggregated in a round-robin order instead of using a static hash. The
stateless traffic includes non-IP L2 traffic, ICMP, some UDP protocols,
and so on.
- When creating a new VLAN, if this parameter is not specified, the default
of (no) is used.
version_added: 2.5
choices: [yes, no]
notes: notes:
- Requires the f5-sdk Python package on the host. This is as easy as pip
install f5-sdk.
- Requires BIG-IP versions >= 12.0.0 - Requires BIG-IP versions >= 12.0.0
extends_documentation_fragment: f5 extends_documentation_fragment: f5
requirements:
- f5-sdk
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
- Wojciech Wypior (@wojtek0806) - Wojciech Wypior (@wojtek0806)
@ -114,162 +147,296 @@ EXAMPLES = r'''
RETURN = r''' RETURN = r'''
description: description:
description: The description set on the VLAN description: The description set on the VLAN.
returned: changed returned: changed
type: string type: string
sample: foo VLAN sample: foo VLAN
interfaces: interfaces:
description: Interfaces that the VLAN is assigned to description: Interfaces that the VLAN is assigned to.
returned: changed returned: changed
type: list type: list
sample: ['1.1','1.2'] sample: ['1.1','1.2']
name:
description: The name of the VLAN
returned: changed
type: string
sample: net1
partition: partition:
description: The partition that the VLAN was created on description: The partition that the VLAN was created on.
returned: changed returned: changed
type: string type: string
sample: Common sample: Common
tag: tag:
description: The ID of the VLAN description: The ID of the VLAN.
returned: changed returned: changed
type: int type: int
sample: 2345 sample: 2345
cmp_hash:
description: New traffic disaggregation method.
returned: changed
type: string
sample: source-address
dag_tunnel:
description: The new DAG tunnel setting.
returned: changed
type: string
sample: outer
''' '''
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 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:
HAS_F5SDK = 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
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
def __init__(self, params=None): api_map = {
self._values = defaultdict(lambda: None) 'cmpHash': 'cmp_hash',
if params: 'dagTunnel': 'dag_tunnel',
self.update(params=params) 'dagRoundRobin': 'dag_round_robin'
}
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
updatables = [ updatables = [
'tagged_interfaces', 'untagged_interfaces', 'tag', 'tagged_interfaces', 'untagged_interfaces', 'tag',
'description' 'description', 'mtu', 'cmp_hash', 'dag_tunnel',
'dag_round_robin'
] ]
returnables = [ returnables = [
'description', 'partition', 'name', 'tag', 'interfaces', 'description', 'partition', 'tag', 'interfaces',
'tagged_interfaces', 'untagged_interfaces' 'tagged_interfaces', 'untagged_interfaces', 'mtu',
'cmp_hash', 'dag_tunnel', 'dag_round_robin'
] ]
api_attributes = [ api_attributes = [
'description', 'interfaces', 'partition', 'name', 'tag' 'description', 'interfaces', 'tag', 'mtu', 'cmpHash',
'dagTunnel', 'dagRoundRobin'
] ]
api_map = {}
@property def to_return(self):
def interfaces(self): result = {}
tagged = self._values['tagged_interfaces'] for returnable in self.returnables:
untagged = self._values['untagged_interfaces'] result[returnable] = getattr(self, returnable)
if tagged: result = self._filter_params(result)
return [dict(name=x, tagged=True) for x in tagged] return result
if untagged:
return [dict(name=x, untagged=True) for x in untagged]
class ApiParameters(Parameters):
@property @property
def tagged_interfaces(self): def tagged_interfaces(self):
value = self._values['tagged_interfaces'] if self._values['interfaces'] is None:
if value is None: return None
result = [str(x.name) for x in self._values['interfaces'] if x.tagged is True]
result = sorted(result)
return result
@property
def untagged_interfaces(self):
if self._values['interfaces'] is None:
return None return None
ifcs = self._parse_return_ifcs() result = [str(x.name) for x in self._values['interfaces'] if x.untagged is True]
for ifc in value: result = sorted(result)
if ifc not in ifcs: return result
err = 'The specified interface "%s" was not found' % ifc
raise F5ModuleError(err)
return value
class ModuleParameters(Parameters):
@property @property
def untagged_interfaces(self): def untagged_interfaces(self):
value = self._values['untagged_interfaces'] if self._values['untagged_interfaces'] is None:
if value is None: return None
if self._values['untagged_interfaces'] is None:
return None
if len(self._values['untagged_interfaces']) == 1 and self._values['untagged_interfaces'][0] == '':
return ''
result = sorted([str(x) for x in self._values['untagged_interfaces']])
return result
@property
def tagged_interfaces(self):
if self._values['tagged_interfaces'] is None:
return None
if self._values['tagged_interfaces'] is None:
return None return None
ifcs = self._parse_return_ifcs() if len(self._values['tagged_interfaces']) == 1 and self._values['tagged_interfaces'][0] == '':
for ifc in value: return ''
if ifc not in ifcs: result = sorted([str(x) for x in self._values['tagged_interfaces']])
err = 'The specified interface "%s" was not found' % ifc return result
raise F5ModuleError(err)
return value @property
def mtu(self):
def _get_interfaces_from_device(self): if self._values['mtu'] is None:
lst = self.client.api.tm.net.interfaces.get_collection() return None
return lst if int(self._values['mtu']) < 576 or int(self._values['mtu']) > 9198:
raise F5ModuleError(
def _parse_return_ifcs(self): "The mtu value must be between 576 - 9198"
ifclst = self._get_interfaces_from_device() )
ifcs = [str(x.name) for x in ifclst] return int(self._values['mtu'])
if not ifcs:
err = 'No interfaces were found'
raise F5ModuleError(err)
return ifcs
@property
def cmp_hash(self):
if self._values['cmp_hash'] is None:
return None
if self._values['cmp_hash'] in ['source-address', 'src', 'src-ip', 'source']:
return 'src-ip'
if self._values['cmp_hash'] in ['destination-address', 'dest', 'dst-ip', 'destination', 'dst']:
return 'dst-ip'
else:
return 'default'
@property
def dag_round_robin(self):
if self._values['dag_round_robin'] is None:
return None
if self._values['dag_round_robin'] is True:
return 'enabled'
else:
return 'disabled'
class Changes(Parameters):
def to_return(self): def to_return(self):
result = {} result = {}
for returnable in self.returnables: try:
result[returnable] = getattr(self, returnable) for returnable in self.returnables:
result = self._filter_params(result) result[returnable] = getattr(self, returnable)
result = self._filter_params(result)
except Exception:
pass
return result return result
def api_params(self):
result = {} class UsableChanges(Changes):
for api_attribute in self.api_attributes: pass
if api_attribute in self.api_map:
result[api_attribute] = getattr(
self, self.api_map[api_attribute]) class ReportableChanges(Changes):
else: @property
result[api_attribute] = getattr(self, api_attribute) def tagged_interfaces(self):
result = self._filter_params(result) if self._values['interfaces'] is None:
return None
result = [str(x['name']) for x in self._values['interfaces'] if 'tagged' in x and x['tagged'] is True]
result = sorted(result)
return result
@property
def untagged_interfaces(self):
if self._values['interfaces'] is None:
return None
result = [str(x['name']) for x in self._values['interfaces'] if 'untagged' in x and x['untagged'] is True]
result = sorted(result)
return result
class Difference(object):
def __init__(self, want, have=None):
self.want = want
self.have = have
def compare(self, param):
try:
result = getattr(self, param)
return result
except AttributeError:
return self.__default(param)
def __default(self, param):
attr1 = getattr(self.want, param)
try:
attr2 = getattr(self.have, param)
if attr1 != attr2:
return attr1
except AttributeError:
return attr1
@property
def untagged_interfaces(self):
result = []
if self.want.untagged_interfaces is None:
return None
elif self.want.untagged_interfaces == '' and self.have.untagged_interfaces is None:
return None
elif self.want.untagged_interfaces == '' and len(self.have.untagged_interfaces) > 0:
pass
elif not self.have.untagged_interfaces:
result = dict(
interfaces=[dict(name=x, untagged=True) for x in self.want.untagged_interfaces]
)
elif set(self.want.untagged_interfaces) != set(self.have.untagged_interfaces):
result = dict(
interfaces=[dict(name=x, untagged=True) for x in self.want.untagged_interfaces]
)
else:
return None
return result
@property
def tagged_interfaces(self):
result = []
if self.want.tagged_interfaces is None:
return None
elif self.want.tagged_interfaces == '' and self.have.tagged_interfaces is None:
return None
elif self.want.tagged_interfaces == '' and len(self.have.tagged_interfaces) > 0:
pass
elif not self.have.tagged_interfaces:
result = dict(
interfaces=[dict(name=x, tagged=True) for x in self.want.tagged_interfaces]
)
elif set(self.want.tagged_interfaces) != set(self.have.tagged_interfaces):
result = dict(
interfaces=[dict(name=x, tagged=True) for x in self.want.tagged_interfaces]
)
else:
return None
return 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.have = None self.client = kwargs.get('client', None)
self.want = Parameters() self.want = ModuleParameters(params=self.module.params)
self.want.client = self.client self.have = ApiParameters()
self.want.update(self.client.module.params) self.changes = UsableChanges()
self.changes = Parameters()
def _update_changed_options(self):
diff = Difference(self.want, self.have)
updatables = Parameters.updatables
changed = dict()
for k in updatables:
change = diff.compare(k)
if change is None:
continue
else:
if isinstance(change, dict):
changed.update(change)
else:
changed[k] = change
if changed:
self.changes = UsableChanges(params=changed)
return True
return False
def exec_module(self): def exec_module(self):
changed = False changed = False
@ -284,39 +451,20 @@ class ModuleManager(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(result)
return result return result
def _set_changed_options(self): def _announce_deprecations(self, result):
changed = {} warnings = result.pop('__warnings', [])
for key in Parameters.returnables: for warning in warnings:
if getattr(self.want, key) is not None: self.module.deprecate(
changed[key] = getattr(self.want, key) msg=warning['msg'],
if changed: version=warning['version']
self.changes = Parameters(changed) )
def _update_changed_options(self):
changed = {}
for key in Parameters.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 changed:
self.changes = Parameters(changed)
return True
return False
def _have_interfaces(self, ifcs):
untagged = [str(x.name) for x in ifcs if hasattr(x, 'untagged')]
tagged = [str(x.name) for x in ifcs if hasattr(x, 'tagged')]
if untagged:
self.have.update({'untagged_interfaces': untagged})
if tagged:
self.have.update({'tagged_interfaces': tagged})
def present(self): def present(self):
if self.exists(): if self.exists():
@ -336,18 +484,16 @@ class ModuleManager(object):
return False return False
def update(self): def update(self):
self.have, ifcs = self.read_current_from_device() self.have = self.read_current_from_device()
if ifcs:
self._have_interfaces(ifcs)
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():
@ -355,49 +501,59 @@ class ModuleManager(object):
return True return True
def create(self): def create(self):
self._set_changed_options() self.have = ApiParameters()
if self.client.check_mode: if self.want.mtu is None:
self.want.update({'mtu': 1500})
self._update_changed_options()
if self.module.check_mode:
return True return True
self.create_on_device() self.create_on_device()
return True return True
def create_on_device(self): def create_on_device(self):
params = self.want.api_params() params = self.changes.api_params()
self.client.api.tm.net.vlans.vlan.create(**params) self.client.api.tm.net.vlans.vlan.create(
name=self.want.name,
partition=self.want.partition,
**params
)
def update_on_device(self): def update_on_device(self):
params = self.want.api_params() params = self.changes.api_params()
result = self.client.api.tm.net.vlans.vlan.load( resource = self.client.api.tm.net.vlans.vlan.load(
name=self.want.name, partition=self.want.partition name=self.want.name,
partition=self.want.partition
) )
result.modify(**params) resource.modify(**params)
def exists(self): def exists(self):
return self.client.api.tm.net.vlans.vlan.exists( return self.client.api.tm.net.vlans.vlan.exists(
name=self.want.name, partition=self.want.partition name=self.want.name,
partition=self.want.partition
) )
def remove_from_device(self): def remove_from_device(self):
result = self.client.api.tm.net.vlans.vlan.load( resource = self.client.api.tm.net.vlans.vlan.load(
name=self.want.name, partition=self.want.partition name=self.want.name,
partition=self.want.partition
) )
if result: if resource:
result.delete() resource.delete()
def read_current_from_device(self): def read_current_from_device(self):
tmp_res = self.client.api.tm.net.vlans.vlan.load( resource = self.client.api.tm.net.vlans.vlan.load(
name=self.want.name, partition=self.want.partition name=self.want.name, partition=self.want.partition
) )
ifcs = tmp_res.interfaces_s.get_collection() interfaces = resource.interfaces_s.get_collection()
result = resource.attrs
result = tmp_res.attrs result['interfaces'] = interfaces
return Parameters(result), ifcs 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( name=dict(
required=True, required=True,
), ),
@ -412,32 +568,56 @@ class ArgumentSpec(object):
description=dict(), description=dict(),
tag=dict( tag=dict(
type='int' type='int'
),
mtu=dict(type='int'),
cmp_hash=dict(
choices=[
'default',
'destination-address', 'dest', 'dst-ip', 'destination', 'dst',
'source-address', 'src', 'src-ip', 'source'
]
),
dag_tunnel=dict(
choices=['inner', 'outer']
),
dag_round_robin=dict(type='bool'),
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)
self.mutually_exclusive = [
['tagged_interfaces', 'untagged_interfaces']
]
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, mutually_exclusive=spec.mutually_exclusive
mutually_exclusive=[
['tagged_interfaces', 'untagged_interfaces']
]
) )
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)
module.exit_json(**results)
except F5ModuleError as e: except F5ModuleError as e:
client.module.fail_json(msg=str(e)) cleanup_tokens(client)
module.fail_json(msg=str(e))
if __name__ == '__main__': if __name__ == '__main__':

@ -40,11 +40,6 @@ options:
msg: msg:
description: description:
- This overrides the normal error message from a failure to meet the required conditions. - This overrides the normal error message from a failure to meet the required conditions.
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)
@ -84,133 +79,50 @@ import signal
import time import time
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.f5_utils import AnsibleF5Client
from ansible.module_utils.f5_utils import AnsibleF5Parameters HAS_DEVEL_IMPORTS = False
from ansible.module_utils.f5_utils import HAS_F5SDK
from ansible.module_utils.f5_utils import F5ModuleError
from ansible.module_utils.f5_utils import F5_COMMON_ARGS
from ansible.module_utils.six import iteritems
from collections import defaultdict
try: try:
from f5.bigip import ManagementRoot as BigIpMgmt # 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:
HAS_F5SDK = 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
def hard_timeout(client, want, start): def hard_timeout(module, want, start):
elapsed = datetime.datetime.utcnow() - start elapsed = datetime.datetime.utcnow() - start
client.module.fail_json( module.fail_json(
want.msg or "Timeout when waiting for BIG-IP", elapsed=elapsed.seconds want.msg or "Timeout when waiting for BIG-IP", elapsed=elapsed.seconds
) )
class AnsibleF5ClientStub(AnsibleF5Client):
"""Interim class to disconnect Params from connection
This module is an interim class that was made to separate the Ansible Module
Parameters from the connection to BIG-IP.
Since this module needs to be able to control the connection process, the default
class is not appropriate. Therefore, we overload it and re-define out the
connection related work to a separate method.
This class should serve as a reason to break apart this work itself into separate
classes in module_utils. There will be on-going work to do this and, when done,
the result will replace this work here.
"""
def __init__(self, argument_spec=None, supports_check_mode=False,
mutually_exclusive=None, required_together=None,
required_if=None, required_one_of=None, add_file_common_args=False,
f5_product_name='bigip'):
self.f5_product_name = f5_product_name
merged_arg_spec = dict()
merged_arg_spec.update(F5_COMMON_ARGS)
if argument_spec:
merged_arg_spec.update(argument_spec)
self.arg_spec = merged_arg_spec
mutually_exclusive_params = []
if mutually_exclusive:
mutually_exclusive_params += mutually_exclusive
required_together_params = []
if required_together:
required_together_params += required_together
self.module = AnsibleModule(
argument_spec=merged_arg_spec,
supports_check_mode=supports_check_mode,
mutually_exclusive=mutually_exclusive_params,
required_together=required_together_params,
required_if=required_if,
required_one_of=required_one_of,
add_file_common_args=add_file_common_args
)
self.check_mode = self.module.check_mode
self._connect_params = self._get_connect_params()
def connect(self):
try:
if 'transport' not in self.module.params or self.module.params['transport'] != 'cli':
self.api = self._get_mgmt_root(
self.f5_product_name, **self._connect_params
)
return True
except Exception:
return False
def _get_mgmt_root(self, type, **kwargs):
if type == 'bigip':
result = BigIpMgmt(
kwargs['server'],
kwargs['user'],
kwargs['password'],
port=kwargs['server_port'],
timeout=1,
token='tmos'
)
return result
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
returnables = [ returnables = [
'elapsed' 'elapsed'
] ]
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
def to_return(self): def to_return(self):
result = {} result = {}
try: try:
@ -245,10 +157,11 @@ class Changes(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.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 exec_module(self): def exec_module(self):
@ -268,7 +181,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']
) )
@ -276,7 +189,7 @@ class ModuleManager(object):
def execute(self): def execute(self):
signal.signal( signal.signal(
signal.SIGALRM, signal.SIGALRM,
lambda sig, frame: hard_timeout(self.client, self.want, start) lambda sig, frame: hard_timeout(self.module, self.want, start)
) )
# setup handler before scheduling signal, to eliminate a race # setup handler before scheduling signal, to eliminate a race
@ -291,8 +204,8 @@ class ModuleManager(object):
try: try:
# The first test verifies that the REST API is available; this is done # The first test verifies that the REST API is available; this is done
# by repeatedly trying to login to it. # by repeatedly trying to login to it.
connected = self._connect_to_device() self.client = F5Client(**self.module.params)
if not connected: if not self.client:
continue continue
if self._device_is_rebooting(): if self._device_is_rebooting():
@ -333,17 +246,13 @@ class ModuleManager(object):
continue continue
else: else:
elapsed = datetime.datetime.utcnow() - start elapsed = datetime.datetime.utcnow() - start
self.client.module.fail_json( self.module.fail_json(
msg=self.want.msg or "Timeout when waiting for BIG-IP", elapsed=elapsed.seconds msg=self.want.msg or "Timeout when waiting for BIG-IP", elapsed=elapsed.seconds
) )
elapsed = datetime.datetime.utcnow() - start elapsed = datetime.datetime.utcnow() - start
self.changes.update({'elapsed': elapsed.seconds}) self.changes.update({'elapsed': elapsed.seconds})
return False return False
def _connect_to_device(self):
result = self.client.connect()
return result
def _device_is_rebooting(self): def _device_is_rebooting(self):
output = self.client.api.tm.util.bash.exec_cmd( output = self.client.api.tm.util.bash.exec_cmd(
'run', 'run',
@ -386,45 +295,33 @@ 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(
timeout=dict(default=7200, type='int'), timeout=dict(default=7200, type='int'),
delay=dict(default=0, type='int'), delay=dict(default=0, type='int'),
sleep=dict(default=1, type='int'), sleep=dict(default=1, type='int'),
msg=dict() msg=dict()
) )
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 = AnsibleF5ClientStub( 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) mm = ModuleManager(module=module)
results = mm.exec_module() results = mm.exec_module()
cleanup_tokens(client) module.exit_json(**results)
client.module.exit_json(**results)
except F5ModuleError as e: except F5ModuleError as e:
cleanup_tokens(client) module.fail_json(msg=str(e))
client.module.fail_json(msg=str(e))
if __name__ == '__main__': if __name__ == '__main__':

@ -89,17 +89,37 @@ description:
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
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.bigiq import HAS_F5SDK
from library.module_utils.network.f5.bigiq 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_F5SDK = False # Upstream Ansible
from ansible.module_utils.network.f5.bigiq import HAS_F5SDK
from ansible.module_utils.network.f5.bigiq 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
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
@ -119,36 +139,6 @@ class Parameters(AnsibleF5Parameters):
'description' 'description'
] ]
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 = {}
try: try:
@ -159,16 +149,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
class ApiParameters(Parameters): class ApiParameters(Parameters):
pass pass
@ -230,10 +210,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.update(dict(client=client)) self.want = ModuleParameters(client=self.client, params=self.module.params)
self.have = ApiParameters() self.have = ApiParameters()
self.changes = UsableChanges() self.changes = UsableChanges()
@ -243,7 +223,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)
@ -259,7 +239,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
@ -282,7 +262,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))
@ -292,7 +272,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']
) )
@ -316,13 +296,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():
@ -331,7 +311,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
if self.want.accept_eula is False: if self.want.accept_eula is False:
raise F5ModuleError( raise F5ModuleError(
@ -401,42 +381,48 @@ class ModuleManager(object):
if resource is None: if resource is None:
return False return False
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(
regkey_pool=dict(required=True), regkey_pool=dict(required=True),
license_key=dict(required=True, no_log=True), license_key=dict(required=True, no_log=True),
description=dict(), description=dict(),
accept_eula=dict(type='bool') accept_eula=dict(type='bool'),
state=dict(
default='present',
choices=['present', 'absent']
),
) )
self.f5_product_name = 'bigiq' self.argument_spec = {}
self.argument_spec.update(f5_argument_spec)
self.argument_spec.update(argument_spec)
self.required_if = [ self.required_if = [
['state', 'present', ['accept_eula']] ['state', 'present', ['accept_eula']]
] ]
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 required_if=spec.required_if
) )
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) module.exit_json(**results)
except F5ModuleError as e: except F5ModuleError as e:
client.module.fail_json(msg=str(e)) module.fail_json(msg=str(e))
if __name__ == '__main__': if __name__ == '__main__':

@ -69,18 +69,37 @@ description:
sample: My description sample: My description
''' '''
from ansible.module_utils.basic import AnsibleModule
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.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.bigiq import HAS_F5SDK
from library.module_utils.network.f5.bigiq 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_F5SDK = False # Upstream Ansible
from ansible.module_utils.network.f5.bigiq import HAS_F5SDK
from ansible.module_utils.network.f5.bigiq 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
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
@ -100,36 +119,6 @@ class Parameters(AnsibleF5Parameters):
'description' 'description'
] ]
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 = {}
try: try:
@ -140,16 +129,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
class ModuleParameters(Parameters): class ModuleParameters(Parameters):
@property @property
@ -214,10 +193,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(self.client.module.params) self.client = kwargs.get('client', None)
self.want.update({'client': client}) self.want = ModuleParameters(client=self.client, params=self.module.params)
self.have = ApiParameters() self.have = ApiParameters()
self.changes = UsableChanges() self.changes = UsableChanges()
@ -227,7 +206,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)
@ -243,7 +222,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)
return True return True
return False return False
@ -266,7 +245,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))
@ -276,7 +255,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']
) )
@ -297,13 +276,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():
@ -312,7 +291,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
@ -348,13 +327,13 @@ class ModuleManager(object):
id=self.want.uuid id=self.want.uuid
) )
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(),
state=dict( state=dict(
@ -362,27 +341,28 @@ class ArgumentSpec(object):
choices=['absent', 'present'] choices=['absent', 'present']
) )
) )
self.f5_product_name = 'bigiq' 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) module.exit_json(**results)
except F5ModuleError as e: except F5ModuleError as e:
client.module.fail_json(msg=str(e)) module.fail_json(msg=str(e))
if __name__ == '__main__': if __name__ == '__main__':

@ -41,18 +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_sys_db.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_ucs.py E321
lib/ansible/modules/network/f5/bigip_user.py E321
lib/ansible/modules/network/f5/bigip_vcmp_guest.py E321
lib/ansible/modules/network/f5/bigip_virtual_address.py E321
lib/ansible/modules/network/f5/bigip_virtual_server.py E321
lib/ansible/modules/network/f5/bigip_vlan.py E321
lib/ansible/modules/network/f5/bigip_wait.py E321
lib/ansible/modules/network/f5/bigiq_regkey_license.py E321
lib/ansible/modules/network/f5/bigiq_regkey_pool.py E321
lib/ansible/modules/network/illumos/dladm_linkprop.py E317 lib/ansible/modules/network/illumos/dladm_linkprop.py E317
lib/ansible/modules/network/illumos/ipadm_addrprop.py E317 lib/ansible/modules/network/illumos/ipadm_addrprop.py E317
lib/ansible/modules/network/illumos/ipadm_ifprop.py E317 lib/ansible/modules/network/illumos/ipadm_ifprop.py E317

@ -0,0 +1,15 @@
{
"kind": "tm:net:vlan:interfaces:interfacescollectionstate",
"selfLink": "https://localhost/mgmt/tm/net/vlan/~Common~vlan1/interfaces?ver=13.0.0",
"items": [
{
"kind": "tm:net:vlan:interfaces:interfacesstate",
"name": "1.2",
"fullPath": "1.2",
"generation": 105,
"selfLink": "https://localhost/mgmt/tm/net/vlan/~Common~vlan1/interfaces/1.2?ver=13.0.0",
"tagMode": "none",
"tagged": 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_sys_db import Parameters from library.bigip_sys_db import Parameters
from library.bigip_sys_db import ModuleManager from library.bigip_sys_db import ModuleManager
from library.bigip_sys_db import ArgumentSpec from library.bigip_sys_db 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_sys_db import Parameters from ansible.modules.network.f5.bigip_sys_db import Parameters
from ansible.modules.network.f5.bigip_sys_db import ModuleManager from ansible.modules.network.f5.bigip_sys_db import ModuleManager
from ansible.modules.network.f5.bigip_sys_db import ArgumentSpec from ansible.modules.network.f5.bigip_sys_db 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")
@ -66,7 +68,7 @@ class TestParameters(unittest.TestCase):
server='localhost', server='localhost',
user='admin' user='admin'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.key == 'foo' assert p.key == 'foo'
assert p.value == 'bar' assert p.value == 'bar'
@ -80,13 +82,11 @@ class TestParameters(unittest.TestCase):
user='admin' user='admin'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.key == 'foo' assert p.key == 'foo'
assert p.value == 'bar' assert p.value == 'bar'
@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):
@ -118,12 +118,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.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)

@ -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_sys_global import ApiParameters from library.bigip_sys_global import ApiParameters
from library.bigip_sys_global import ModuleParameters from library.bigip_sys_global import ModuleParameters
from library.bigip_sys_global import ModuleManager from library.bigip_sys_global import ModuleManager
from library.bigip_sys_global import ArgumentSpec from library.bigip_sys_global 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_sys_global import ModuleParameters from ansible.modules.network.f5.bigip_sys_global import ModuleParameters
from ansible.modules.network.f5.bigip_sys_global import ModuleManager from ansible.modules.network.f5.bigip_sys_global import ModuleManager
from ansible.modules.network.f5.bigip_sys_global import ArgumentSpec from ansible.modules.network.f5.bigip_sys_global 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")
@ -71,7 +73,7 @@ class TestParameters(unittest.TestCase):
quiet_boot='yes', quiet_boot='yes',
security_banner='yes', security_banner='yes',
) )
p = ModuleParameters(args) p = ModuleParameters(params=args)
assert p.banner_text == 'this is a banner' assert p.banner_text == 'this is a banner'
assert p.console_timeout == 100 assert p.console_timeout == 100
assert p.gui_setup == 'enabled' assert p.gui_setup == 'enabled'
@ -83,7 +85,7 @@ class TestParameters(unittest.TestCase):
def test_api_parameters(self): def test_api_parameters(self):
args = load_fixture('load_sys_global_settings.json') args = load_fixture('load_sys_global_settings.json')
p = ApiParameters(args) p = ApiParameters(params=args)
assert 'Welcome to the BIG-IP Configuration Utility' in p.banner_text assert 'Welcome to the BIG-IP Configuration Utility' in p.banner_text
assert p.console_timeout == 0 assert p.console_timeout == 0
assert p.gui_setup == 'disabled' assert p.gui_setup == 'disabled'
@ -94,8 +96,6 @@ class TestParameters(unittest.TestCase):
assert p.security_banner == 'enabled' assert p.security_banner == 'enabled'
@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):
@ -113,14 +113,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_global_settings.json')) current = ApiParameters(params=load_fixture('load_sys_global_settings.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(return_value=False) mm.exists = Mock(return_value=False)

@ -18,21 +18,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
from ansible.module_utils.f5_utils import F5ModuleError
try: try:
from library.bigip_traffic_group import Parameters from library.bigip_traffic_group import Parameters
from library.bigip_traffic_group import ModuleManager from library.bigip_traffic_group import ModuleManager
from library.bigip_traffic_group import ArgumentSpec from library.bigip_traffic_group 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_traffic_group import Parameters from ansible.modules.network.f5.bigip_traffic_group import Parameters
from ansible.modules.network.f5.bigip_traffic_group import ModuleManager from ansible.modules.network.f5.bigip_traffic_group import ModuleManager
from ansible.modules.network.f5.bigip_traffic_group import ArgumentSpec from ansible.modules.network.f5.bigip_traffic_group 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 +66,10 @@ class TestParameters(unittest.TestCase):
name='foo' name='foo'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.name == 'foo' assert p.name == '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):
@ -84,13 +83,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,8 +18,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
from ansible.module_utils.f5_utils import F5ModuleError
try: try:
from library.bigip_ucs import Parameters from library.bigip_ucs import Parameters
@ -27,7 +26,8 @@ try:
from library.bigip_ucs import ArgumentSpec from library.bigip_ucs import ArgumentSpec
from library.bigip_ucs import V1Manager from library.bigip_ucs import V1Manager
from library.bigip_ucs import V2Manager from library.bigip_ucs import V2Manager
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:
@ -36,7 +36,8 @@ except ImportError:
from ansible.modules.network.f5.bigip_ucs import ArgumentSpec from ansible.modules.network.f5.bigip_ucs import ArgumentSpec
from ansible.modules.network.f5.bigip_ucs import V1Manager from ansible.modules.network.f5.bigip_ucs import V1Manager
from ansible.modules.network.f5.bigip_ucs import V2Manager from ansible.modules.network.f5.bigip_ucs import V2Manager
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")
@ -76,7 +77,7 @@ class TestParameters(unittest.TestCase):
state='installed' state='installed'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.ucs == '/root/bigip.localhost.localdomain.ucs' assert p.ucs == '/root/bigip.localhost.localdomain.ucs'
assert p.force is True assert p.force is True
assert p.include_chassis_level_config is True assert p.include_chassis_level_config is True
@ -98,7 +99,7 @@ class TestParameters(unittest.TestCase):
reset_trust=False reset_trust=False
) )
p = Parameters(args) p = Parameters(params=args)
assert p.ucs == '/root/bigip.localhost.localdomain.ucs' assert p.ucs == '/root/bigip.localhost.localdomain.ucs'
assert p.include_chassis_level_config is False assert p.include_chassis_level_config is False
assert p.no_license is False assert p.no_license is False
@ -107,8 +108,6 @@ class TestParameters(unittest.TestCase):
assert p.install_command == "tmsh load sys ucs /var/local/ucs/bigip.localhost.localdomain.ucs" assert p.install_command == "tmsh load sys ucs /var/local/ucs/bigip.localhost.localdomain.ucs"
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestV1Manager(unittest.TestCase): class TestV1Manager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -122,17 +121,16 @@ class TestV1Manager(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_v1 = Mock(return_value=True) mm.is_version_v1 = Mock(return_value=True)
vm = V1Manager(client) vm = V1Manager(module=module)
vm.create_on_device = Mock(return_value=True) vm.create_on_device = Mock(return_value=True)
vm.exists = Mock(side_effect=[False, True]) vm.exists = Mock(side_effect=[False, True])
@ -149,17 +147,16 @@ class TestV1Manager(unittest.TestCase):
state='present' state='present'
)) ))
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_v1 = Mock(return_value=True) mm.is_version_v1 = Mock(return_value=True)
vm = V1Manager(client) vm = V1Manager(module=module)
vm.create_on_device = Mock(return_value=True) vm.create_on_device = Mock(return_value=True)
vm.exists = Mock(side_effect=[False, True]) vm.exists = Mock(side_effect=[False, True])
@ -176,17 +173,16 @@ class TestV1Manager(unittest.TestCase):
state='installed' state='installed'
)) ))
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_v1 = Mock(return_value=True) mm.is_version_v1 = Mock(return_value=True)
vm = V1Manager(client) vm = V1Manager(module=module)
vm.create_on_device = Mock(return_value=True) vm.create_on_device = Mock(return_value=True)
vm.exists = Mock(return_value=True) vm.exists = Mock(return_value=True)
vm.install_on_device = Mock(return_value=True) vm.install_on_device = Mock(return_value=True)
@ -204,17 +200,16 @@ class TestV1Manager(unittest.TestCase):
state='absent' state='absent'
)) ))
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_v1 = Mock(return_value=True) mm.is_version_v1 = Mock(return_value=True)
vm = V1Manager(client) vm = V1Manager(module=module)
vm.remove_from_device = Mock(return_value=True) vm.remove_from_device = Mock(return_value=True)
vm.exists = Mock(side_effect=[True, False]) vm.exists = Mock(side_effect=[True, False])
@ -231,17 +226,16 @@ class TestV1Manager(unittest.TestCase):
state='absent' state='absent'
)) ))
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_v1 = Mock(return_value=True) mm.is_version_v1 = Mock(return_value=True)
vm = V1Manager(client) vm = V1Manager(module=module)
vm.remove_from_device = Mock(return_value=True) vm.remove_from_device = Mock(return_value=True)
vm.exists = Mock(side_effect=[True, True]) vm.exists = Mock(side_effect=[True, True])
@ -250,8 +244,6 @@ class TestV1Manager(unittest.TestCase):
assert 'Failed to delete' in str(ex.value) assert 'Failed to delete' in str(ex.value)
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestV2Manager(unittest.TestCase): class TestV2Manager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -265,17 +257,16 @@ class TestV2Manager(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_v1 = Mock(return_value=False) mm.is_version_v1 = Mock(return_value=False)
vm = V2Manager(client) vm = V2Manager(module=module)
vm.create_on_device = Mock(return_value=True) vm.create_on_device = Mock(return_value=True)
vm.exists = Mock(side_effect=[False, True]) vm.exists = Mock(side_effect=[False, True])
@ -292,17 +283,16 @@ class TestV2Manager(unittest.TestCase):
state='present' state='present'
)) ))
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_v1 = Mock(return_value=False) mm.is_version_v1 = Mock(return_value=False)
vm = V2Manager(client) vm = V2Manager(module=module)
vm.create_on_device = Mock(return_value=True) vm.create_on_device = Mock(return_value=True)
vm.exists = Mock(side_effect=[False, True]) vm.exists = Mock(side_effect=[False, True])
@ -319,17 +309,16 @@ class TestV2Manager(unittest.TestCase):
state='installed' state='installed'
)) ))
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_v1 = Mock(return_value=False) mm.is_version_v1 = Mock(return_value=False)
vm = V2Manager(client) vm = V2Manager(module=module)
vm.create_on_device = Mock(return_value=True) vm.create_on_device = Mock(return_value=True)
vm.exists = Mock(return_value=True) vm.exists = Mock(return_value=True)
vm.install_on_device = Mock(return_value=True) vm.install_on_device = Mock(return_value=True)
@ -347,17 +336,16 @@ class TestV2Manager(unittest.TestCase):
state='absent' state='absent'
)) ))
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_v1 = Mock(return_value=False) mm.is_version_v1 = Mock(return_value=False)
vm = V1Manager(client) vm = V1Manager(module=module)
vm.remove_from_device = Mock(return_value=True) vm.remove_from_device = Mock(return_value=True)
vm.exists = Mock(side_effect=[True, False]) vm.exists = Mock(side_effect=[True, False])
@ -374,17 +362,16 @@ class TestV2Manager(unittest.TestCase):
state='absent' state='absent'
)) ))
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_v1 = Mock(return_value=False) mm.is_version_v1 = Mock(return_value=False)
vm = V1Manager(client) vm = V1Manager(module=module)
vm.remove_from_device = Mock(return_value=True) vm.remove_from_device = Mock(return_value=True)
vm.exists = Mock(side_effect=[True, True]) vm.exists = Mock(side_effect=[True, True])

@ -18,8 +18,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
from ansible.module_utils.f5_utils import F5ModuleError
try: try:
from library.bigip_user import Parameters from library.bigip_user import Parameters
@ -27,7 +26,8 @@ try:
from library.bigip_user import ArgumentSpec from library.bigip_user import ArgumentSpec
from library.bigip_user import UnparitionedManager from library.bigip_user import UnparitionedManager
from library.bigip_user import PartitionedManager from library.bigip_user import PartitionedManager
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:
@ -36,7 +36,8 @@ except ImportError:
from ansible.modules.network.f5.bigip_user import ArgumentSpec from ansible.modules.network.f5.bigip_user import ArgumentSpec
from ansible.modules.network.f5.bigip_user import UnparitionedManager from ansible.modules.network.f5.bigip_user import UnparitionedManager
from ansible.modules.network.f5.bigip_user import PartitionedManager from ansible.modules.network.f5.bigip_user import PartitionedManager
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")
@ -74,7 +75,7 @@ class TestParameters(unittest.TestCase):
update_password='always' update_password='always'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.username_credential == 'someuser' assert p.username_credential == 'someuser'
assert p.password_credential == 'testpass' assert p.password_credential == 'testpass'
assert p.full_name == 'Fake Person' assert p.full_name == 'Fake Person'
@ -91,7 +92,7 @@ class TestParameters(unittest.TestCase):
shell='none' shell='none'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.name == 'someuser' assert p.name == 'someuser'
assert p.password == 'testpass' assert p.password == 'testpass'
assert p.full_name == 'Fake Person' assert p.full_name == 'Fake Person'
@ -99,8 +100,6 @@ class TestParameters(unittest.TestCase):
assert p.shell == 'none' assert p.shell == 'none'
@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):
@ -118,21 +117,21 @@ class TestManager(unittest.TestCase):
update_password='on_create' update_password='on_create'
)) ))
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) pm = PartitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=False)
pm = PartitionedManager(client)
pm.create_on_device = Mock(return_value=True) pm.create_on_device = Mock(return_value=True)
pm.exists = Mock(return_value=False) pm.exists = Mock(return_value=False)
results = pm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=False)
mm.get_manager = Mock(return_value=pm)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['partition_access'] == access assert results['partition_access'] == access
@ -147,21 +146,21 @@ 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) pm = PartitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=False)
pm = PartitionedManager(client)
pm.create_on_device = Mock(return_value=True) pm.create_on_device = Mock(return_value=True)
pm.exists = Mock(return_value=False) pm.exists = Mock(return_value=False)
results = pm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=False)
mm.get_manager = Mock(return_value=pm)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['partition_access'] == access assert results['partition_access'] == access
@ -177,26 +176,26 @@ 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) pm = PartitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=False)
pm = PartitionedManager(client)
pm.create_on_device = Mock(return_value=True) pm.create_on_device = Mock(return_value=True)
pm.exists = Mock(return_value=False) pm.exists = Mock(return_value=False)
mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=False)
mm.get_manager = Mock(return_value=pm)
msg = "The 'update_password' option " \ msg = "The 'update_password' option " \
"needs to be set to 'on_create' when creating " \ "needs to be set to 'on_create' when creating " \
"a resource with a password." "a resource with a password."
with pytest.raises(F5ModuleError) as ex: with pytest.raises(F5ModuleError) as ex:
pm.exec_module() mm.exec_module()
assert str(ex.value) == msg assert str(ex.value) == msg
def test_create_user_partition_access_raises(self, *args): def test_create_user_partition_access_raises(self, *args):
@ -207,25 +206,25 @@ 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) pm = PartitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=False)
pm = PartitionedManager(client)
pm.create_on_device = Mock(return_value=True) pm.create_on_device = Mock(return_value=True)
pm.exists = Mock(return_value=False) pm.exists = Mock(return_value=False)
mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=False)
mm.get_manager = Mock(return_value=pm)
msg = "The 'partition_access' option " \ msg = "The 'partition_access' option " \
"is required when creating a resource." "is required when creating a resource."
with pytest.raises(F5ModuleError) as ex: with pytest.raises(F5ModuleError) as ex:
pm.exec_module() mm.exec_module()
assert str(ex.value) == msg assert str(ex.value) == msg
def test_create_user_shell_bash(self, *args): def test_create_user_shell_bash(self, *args):
@ -241,21 +240,21 @@ class TestManager(unittest.TestCase):
shell='bash' shell='bash'
)) ))
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) pm = PartitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=False)
pm = PartitionedManager(client)
pm.create_on_device = Mock(return_value=True) pm.create_on_device = Mock(return_value=True)
pm.exists = Mock(return_value=False) pm.exists = Mock(return_value=False)
results = pm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=False)
mm.get_manager = Mock(return_value=pm)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['partition_access'] == access assert results['partition_access'] == access
@ -273,25 +272,25 @@ class TestManager(unittest.TestCase):
shell='bash' shell='bash'
)) ))
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) pm = PartitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=False)
pm = PartitionedManager(client)
pm.create_on_device = Mock(return_value=True) pm.create_on_device = Mock(return_value=True)
pm.exists = Mock(return_value=False) pm.exists = Mock(return_value=False)
mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=False)
mm.get_manager = Mock(return_value=pm)
msg = "Shell access is only available to 'admin' or " \ msg = "Shell access is only available to 'admin' or " \
"'resource-admin' roles" "'resource-admin' roles"
with pytest.raises(F5ModuleError) as ex: with pytest.raises(F5ModuleError) as ex:
pm.exec_module() mm.exec_module()
assert str(ex.value) == msg assert str(ex.value) == msg
def test_update_user_password_no_pass(self, *args): def test_update_user_password_no_pass(self, *args):
@ -303,26 +302,26 @@ 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
) )
# 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 = Parameters(load_fixture('load_auth_user_no_pass.json')) current = Parameters(params=load_fixture('load_auth_user_no_pass.json'))
# 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) pm = PartitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=False)
pm = PartitionedManager(client)
pm.exists = Mock(return_value=True) pm.exists = Mock(return_value=True)
pm.update_on_device = Mock(return_value=True) pm.update_on_device = Mock(return_value=True)
pm.read_current_from_device = Mock(return_value=current) pm.read_current_from_device = Mock(return_value=current)
results = pm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=False)
mm.get_manager = Mock(return_value=pm)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
@ -335,26 +334,26 @@ 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
) )
# 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 = Parameters(load_fixture('load_auth_user_with_pass.json')) current = Parameters(params=load_fixture('load_auth_user_with_pass.json'))
# 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) pm = PartitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=False)
pm = PartitionedManager(client)
pm.exists = Mock(return_value=True) pm.exists = Mock(return_value=True)
pm.update_on_device = Mock(return_value=True) pm.update_on_device = Mock(return_value=True)
pm.read_current_from_device = Mock(return_value=current) pm.read_current_from_device = Mock(return_value=current)
results = pm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=False)
mm.get_manager = Mock(return_value=pm)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
@ -367,31 +366,31 @@ class TestManager(unittest.TestCase):
shell='none' shell='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
) )
# 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 = Parameters( current = Parameters(
dict( params=dict(
user='admin', user='admin',
shell='tmsh' shell='tmsh'
) )
) )
# 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) pm = PartitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=False)
pm = PartitionedManager(client)
pm.exists = Mock(return_value=True) pm.exists = Mock(return_value=True)
pm.update_on_device = Mock(return_value=True) pm.update_on_device = Mock(return_value=True)
pm.read_current_from_device = Mock(return_value=current) pm.read_current_from_device = Mock(return_value=current)
results = pm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=False)
mm.get_manager = Mock(return_value=pm)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['shell'] == 'none' assert results['shell'] == 'none'
@ -405,32 +404,32 @@ class TestManager(unittest.TestCase):
shell='none' shell='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
) )
# Configure the parameters that would be returned by querying the # Configure the parameters that would be returned by querying the
# remote device # remote device
access = [{'name': 'Common', 'role': 'guest'}] access = [{'name': 'Common', 'role': 'guest'}]
current = Parameters( current = Parameters(
dict( params=dict(
user='admin', user='admin',
partition_access=access partition_access=access
) )
) )
# 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) pm = PartitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=False)
pm = PartitionedManager(client)
pm.exists = Mock(return_value=True) pm.exists = Mock(return_value=True)
pm.update_on_device = Mock(return_value=True) pm.update_on_device = Mock(return_value=True)
pm.read_current_from_device = Mock(return_value=current) pm.read_current_from_device = Mock(return_value=current)
results = pm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=False)
mm.get_manager = Mock(return_value=pm)
results = mm.exec_module()
assert results['changed'] is False assert results['changed'] is False
assert not hasattr(results, 'shell') assert not hasattr(results, 'shell')
@ -444,17 +443,16 @@ class TestManager(unittest.TestCase):
shell='bash' shell='bash'
)) ))
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
) )
# Configure the parameters that would be returned by querying the # Configure the parameters that would be returned by querying the
# remote device # remote device
access = [{'name': 'all', 'role': 'admin'}] access = [{'name': 'all', 'role': 'admin'}]
current = Parameters( current = Parameters(
dict( params=dict(
user='admin', user='admin',
shell='tmsh', shell='tmsh',
partition_access=access partition_access=access
@ -462,15 +460,16 @@ class TestManager(unittest.TestCase):
) )
# 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) upm = UnparitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=True)
upm = UnparitionedManager(client)
upm.exists = Mock(return_value=True) upm.exists = Mock(return_value=True)
upm.update_on_device = Mock(return_value=True) upm.update_on_device = Mock(return_value=True)
upm.read_current_from_device = Mock(return_value=current) upm.read_current_from_device = Mock(return_value=current)
results = upm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=True)
mm.get_manager = Mock(return_value=upm)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['shell'] == 'bash' assert results['shell'] == 'bash'
@ -484,10 +483,9 @@ class TestManager(unittest.TestCase):
shell='bash' shell='bash'
)) ))
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
) )
# Configure the parameters that would be returned by querying the # Configure the parameters that would be returned by querying the
@ -497,7 +495,7 @@ class TestManager(unittest.TestCase):
{'name': 'all', 'role': 'guest'} {'name': 'all', 'role': 'guest'}
] ]
current = Parameters( current = Parameters(
dict( params=dict(
user='admin', user='admin',
shell='tmsh', shell='tmsh',
partition_access=access partition_access=access
@ -505,24 +503,23 @@ class TestManager(unittest.TestCase):
) )
# 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) upm = UnparitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=True)
upm = UnparitionedManager(client)
upm.exists = Mock(return_value=True) upm.exists = Mock(return_value=True)
upm.update_on_device = Mock(return_value=True) upm.update_on_device = Mock(return_value=True)
upm.read_current_from_device = Mock(return_value=current) upm.read_current_from_device = Mock(return_value=current)
mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=True)
mm.get_manager = Mock(return_value=upm)
msg = "Shell access is only available to 'admin' or " \ msg = "Shell access is only available to 'admin' or " \
"'resource-admin' roles" "'resource-admin' roles"
with pytest.raises(F5ModuleError) as ex: with pytest.raises(F5ModuleError) as ex:
upm.exec_module() mm.exec_module()
assert str(ex.value) == msg assert str(ex.value) == msg
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestLegacyManager(unittest.TestCase): class TestLegacyManager(unittest.TestCase):
def setUp(self): def setUp(self):
@ -540,21 +537,21 @@ class TestLegacyManager(unittest.TestCase):
update_password='on_create' update_password='on_create'
)) ))
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) upm = UnparitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=True)
upm = UnparitionedManager(client)
upm.create_on_device = Mock(return_value=True) upm.create_on_device = Mock(return_value=True)
upm.exists = Mock(return_value=False) upm.exists = Mock(return_value=False)
results = upm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=True)
mm.get_manager = Mock(return_value=upm)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['partition_access'] == access assert results['partition_access'] == access
@ -569,21 +566,21 @@ class TestLegacyManager(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) upm = UnparitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=True)
upm = UnparitionedManager(client)
upm.create_on_device = Mock(return_value=True) upm.create_on_device = Mock(return_value=True)
upm.exists = Mock(return_value=False) upm.exists = Mock(return_value=False)
results = upm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=True)
mm.get_manager = Mock(return_value=upm)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['partition_access'] == access assert results['partition_access'] == access
@ -599,26 +596,26 @@ class TestLegacyManager(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) upm = UnparitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=True)
upm = UnparitionedManager(client)
upm.create_on_device = Mock(return_value=True) upm.create_on_device = Mock(return_value=True)
upm.exists = Mock(return_value=False) upm.exists = Mock(return_value=False)
mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=True)
mm.get_manager = Mock(return_value=upm)
msg = "The 'update_password' option " \ msg = "The 'update_password' option " \
"needs to be set to 'on_create' when creating " \ "needs to be set to 'on_create' when creating " \
"a resource with a password." "a resource with a password."
with pytest.raises(F5ModuleError) as ex: with pytest.raises(F5ModuleError) as ex:
upm.exec_module() mm.exec_module()
assert str(ex.value) == msg assert str(ex.value) == msg
def test_create_user_partition_access_raises(self, *args): def test_create_user_partition_access_raises(self, *args):
@ -629,25 +626,25 @@ class TestLegacyManager(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) upm = UnparitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=True)
upm = UnparitionedManager(client)
upm.create_on_device = Mock(return_value=True) upm.create_on_device = Mock(return_value=True)
upm.exists = Mock(return_value=False) upm.exists = Mock(return_value=False)
mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=True)
mm.get_manager = Mock(return_value=upm)
msg = "The 'partition_access' option " \ msg = "The 'partition_access' option " \
"is required when creating a resource." "is required when creating a resource."
with pytest.raises(F5ModuleError) as ex: with pytest.raises(F5ModuleError) as ex:
upm.exec_module() mm.exec_module()
assert str(ex.value) == msg assert str(ex.value) == msg
def test_create_user_shell_bash(self, *args): def test_create_user_shell_bash(self, *args):
@ -663,21 +660,21 @@ class TestLegacyManager(unittest.TestCase):
shell='bash' shell='bash'
)) ))
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) upm = UnparitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=True)
upm = UnparitionedManager(client)
upm.create_on_device = Mock(return_value=True) upm.create_on_device = Mock(return_value=True)
upm.exists = Mock(return_value=False) upm.exists = Mock(return_value=False)
results = upm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=True)
mm.get_manager = Mock(return_value=upm)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['partition_access'] == access assert results['partition_access'] == access
@ -695,25 +692,25 @@ class TestLegacyManager(unittest.TestCase):
shell='bash' shell='bash'
)) ))
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) upm = UnparitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=True)
upm = UnparitionedManager(client)
upm.create_on_device = Mock(return_value=True) upm.create_on_device = Mock(return_value=True)
upm.exists = Mock(return_value=False) upm.exists = Mock(return_value=False)
mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=True)
mm.get_manager = Mock(return_value=upm)
msg = "Shell access is only available to 'admin' or " \ msg = "Shell access is only available to 'admin' or " \
"'resource-admin' roles" "'resource-admin' roles"
with pytest.raises(F5ModuleError) as ex: with pytest.raises(F5ModuleError) as ex:
upm.exec_module() mm.exec_module()
assert str(ex.value) == msg assert str(ex.value) == msg
def test_update_user_password(self, *args): def test_update_user_password(self, *args):
@ -725,32 +722,32 @@ class TestLegacyManager(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
) )
# Configure the parameters that would be returned by querying the # Configure the parameters that would be returned by querying the
# remote device # remote device
access = [{'name': 'Common', 'role': 'guest'}] access = [{'name': 'Common', 'role': 'guest'}]
current = Parameters( current = Parameters(
dict( params=dict(
shell='tmsh', shell='tmsh',
partition_access=access partition_access=access
) )
) )
# 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) upm = UnparitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=True)
upm = UnparitionedManager(client)
upm.exists = Mock(return_value=True) upm.exists = Mock(return_value=True)
upm.update_on_device = Mock(return_value=True) upm.update_on_device = Mock(return_value=True)
upm.read_current_from_device = Mock(return_value=current) upm.read_current_from_device = Mock(return_value=current)
results = upm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=True)
mm.get_manager = Mock(return_value=upm)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
@ -763,31 +760,31 @@ class TestLegacyManager(unittest.TestCase):
shell='none' shell='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
) )
# 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 = Parameters( current = Parameters(
dict( params=dict(
user='admin', user='admin',
shell='tmsh' shell='tmsh'
) )
) )
# 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) upm = UnparitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=True)
upm = UnparitionedManager(client)
upm.exists = Mock(return_value=True) upm.exists = Mock(return_value=True)
upm.update_on_device = Mock(return_value=True) upm.update_on_device = Mock(return_value=True)
upm.read_current_from_device = Mock(return_value=current) upm.read_current_from_device = Mock(return_value=current)
results = upm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=True)
mm.get_manager = Mock(return_value=upm)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['shell'] == 'none' assert results['shell'] == 'none'
@ -801,32 +798,32 @@ class TestLegacyManager(unittest.TestCase):
shell='none' shell='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
) )
# Configure the parameters that would be returned by querying the # Configure the parameters that would be returned by querying the
# remote device # remote device
access = [{'name': 'Common', 'role': 'guest'}] access = [{'name': 'Common', 'role': 'guest'}]
current = Parameters( current = Parameters(
dict( params=dict(
user='admin', user='admin',
partition_access=access partition_access=access
) )
) )
# 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) upm = UnparitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=True)
upm = UnparitionedManager(client)
upm.exists = Mock(return_value=True) upm.exists = Mock(return_value=True)
upm.update_on_device = Mock(return_value=True) upm.update_on_device = Mock(return_value=True)
upm.read_current_from_device = Mock(return_value=current) upm.read_current_from_device = Mock(return_value=current)
results = upm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=True)
mm.get_manager = Mock(return_value=upm)
results = mm.exec_module()
assert results['changed'] is False assert results['changed'] is False
assert not hasattr(results, 'shell') assert not hasattr(results, 'shell')
@ -840,17 +837,16 @@ class TestLegacyManager(unittest.TestCase):
shell='bash' shell='bash'
)) ))
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
) )
# Configure the parameters that would be returned by querying the # Configure the parameters that would be returned by querying the
# remote device # remote device
access = [{'name': 'all', 'role': 'admin'}] access = [{'name': 'all', 'role': 'admin'}]
current = Parameters( current = Parameters(
dict( params=dict(
user='admin', user='admin',
shell='tmsh', shell='tmsh',
partition_access=access partition_access=access
@ -858,15 +854,16 @@ class TestLegacyManager(unittest.TestCase):
) )
# 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) upm = UnparitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=True)
upm = UnparitionedManager(client)
upm.exists = Mock(return_value=True) upm.exists = Mock(return_value=True)
upm.update_on_device = Mock(return_value=True) upm.update_on_device = Mock(return_value=True)
upm.read_current_from_device = Mock(return_value=current) upm.read_current_from_device = Mock(return_value=current)
results = upm.exec_module() mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=True)
mm.get_manager = Mock(return_value=upm)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['shell'] == 'bash' assert results['shell'] == 'bash'
@ -880,10 +877,9 @@ class TestLegacyManager(unittest.TestCase):
shell='bash' shell='bash'
)) ))
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
) )
# Configure the parameters that would be returned by querying the # Configure the parameters that would be returned by querying the
@ -893,7 +889,7 @@ class TestLegacyManager(unittest.TestCase):
{'name': 'all', 'role': 'guest'} {'name': 'all', 'role': 'guest'}
] ]
current = Parameters( current = Parameters(
dict( params=dict(
user='admin', user='admin',
shell='tmsh', shell='tmsh',
partition_access=access partition_access=access
@ -901,17 +897,18 @@ class TestLegacyManager(unittest.TestCase):
) )
# 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) upm = UnparitionedManager(module=module, params=module.params)
mm.is_version_less_than_13 = Mock(return_value=True)
upm = UnparitionedManager(client)
upm.exists = Mock(return_value=True) upm.exists = Mock(return_value=True)
upm.update_on_device = Mock(return_value=True) upm.update_on_device = Mock(return_value=True)
upm.read_current_from_device = Mock(return_value=current) upm.read_current_from_device = Mock(return_value=current)
mm = ModuleManager(module=module)
mm.is_version_less_than_13 = Mock(return_value=True)
mm.get_manager = Mock(return_value=upm)
msg = "Shell access is only available to 'admin' or " \ msg = "Shell access is only available to 'admin' or " \
"'resource-admin' roles" "'resource-admin' roles"
with pytest.raises(F5ModuleError) as ex: with pytest.raises(F5ModuleError) as ex:
upm.exec_module() mm.exec_module()
assert str(ex.value) == msg assert str(ex.value) == msg

@ -18,20 +18,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_vcmp_guest import Parameters from library.bigip_vcmp_guest import Parameters
from library.bigip_vcmp_guest import ModuleManager from library.bigip_vcmp_guest import ModuleManager
from library.bigip_vcmp_guest import ArgumentSpec from library.bigip_vcmp_guest 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_vcmp_guest import Parameters from ansible.modules.network.f5.bigip_vcmp_guest import Parameters
from ansible.modules.network.f5.bigip_vcmp_guest import ModuleManager from ansible.modules.network.f5.bigip_vcmp_guest import ModuleManager
from ansible.modules.network.f5.bigip_vcmp_guest import ArgumentSpec from ansible.modules.network.f5.bigip_vcmp_guest 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")
@ -70,7 +72,7 @@ class TestParameters(unittest.TestCase):
] ]
) )
p = Parameters(args) p = Parameters(params=args)
assert p.initial_image == 'BIGIP-12.1.0.1.0.1447-HF1.iso' assert p.initial_image == 'BIGIP-12.1.0.1.0.1447-HF1.iso'
assert p.mgmt_network == 'bridged' assert p.mgmt_network == 'bridged'
@ -80,7 +82,7 @@ class TestParameters(unittest.TestCase):
mgmt_address='1.2.3.4' mgmt_address='1.2.3.4'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.mgmt_network == 'bridged' assert p.mgmt_network == 'bridged'
assert p.mgmt_address == '1.2.3.4/32' assert p.mgmt_address == '1.2.3.4/32'
@ -90,7 +92,7 @@ class TestParameters(unittest.TestCase):
mgmt_address='1.2.3.4/24' mgmt_address='1.2.3.4/24'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.mgmt_network == 'bridged' assert p.mgmt_network == 'bridged'
assert p.mgmt_address == '1.2.3.4/24' assert p.mgmt_address == '1.2.3.4/24'
@ -100,7 +102,7 @@ class TestParameters(unittest.TestCase):
mgmt_address='1.2.3.4/255.255.255.0' mgmt_address='1.2.3.4/255.255.255.0'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.mgmt_network == 'bridged' assert p.mgmt_network == 'bridged'
assert p.mgmt_address == '1.2.3.4/24' assert p.mgmt_address == '1.2.3.4/24'
@ -109,7 +111,7 @@ class TestParameters(unittest.TestCase):
mgmt_route='1.2.3.4' mgmt_route='1.2.3.4'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.mgmt_route == '1.2.3.4' assert p.mgmt_route == '1.2.3.4'
def test_module_parameters_vcmp_software_image_facts(self): def test_module_parameters_vcmp_software_image_facts(self):
@ -120,7 +122,7 @@ class TestParameters(unittest.TestCase):
initial_image='BIGIP-12.1.0.1.0.1447-HF1.iso/1', initial_image='BIGIP-12.1.0.1.0.1447-HF1.iso/1',
) )
p = Parameters(args) p = Parameters(params=args)
assert p.initial_image == 'BIGIP-12.1.0.1.0.1447-HF1.iso/1' assert p.initial_image == 'BIGIP-12.1.0.1.0.1447-HF1.iso/1'
def test_api_parameters(self): def test_api_parameters(self):
@ -136,7 +138,7 @@ class TestParameters(unittest.TestCase):
] ]
) )
p = Parameters(args) p = Parameters(params=args)
assert p.initial_image == 'BIGIP-tmos-tier2-13.1.0.0.0.931.iso' assert p.initial_image == 'BIGIP-tmos-tier2-13.1.0.0.0.931.iso'
assert p.mgmt_route == '2.2.2.2' assert p.mgmt_route == '2.2.2.2'
assert p.mgmt_address == '1.1.1.1/24' assert p.mgmt_address == '1.1.1.1/24'
@ -144,8 +146,6 @@ class TestParameters(unittest.TestCase):
assert '/Common/vlan2' in p.vlans assert '/Common/vlan2' in p.vlans
@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):
self.spec = ArgumentSpec() self.spec = ArgumentSpec()
@ -161,14 +161,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.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)
mm.is_deployed = Mock(side_effect=[False, True, True, True, True]) mm.is_deployed = Mock(side_effect=[False, True, True, 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_virtual_address import Parameters from library.bigip_virtual_address import Parameters
from library.bigip_virtual_address import ModuleManager from library.bigip_virtual_address import ModuleManager
from library.bigip_virtual_address import ArgumentSpec from library.bigip_virtual_address 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_virtual_address import Parameters from ansible.modules.network.f5.bigip_virtual_address import Parameters
from ansible.modules.network.f5.bigip_virtual_address import ModuleManager from ansible.modules.network.f5.bigip_virtual_address import ModuleManager
from ansible.modules.network.f5.bigip_virtual_address import ArgumentSpec from ansible.modules.network.f5.bigip_virtual_address 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")
@ -70,7 +72,7 @@ class TestParameters(unittest.TestCase):
advertise_route='always', advertise_route='always',
use_route_advertisement='yes' use_route_advertisement='yes'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.state == 'present' assert p.state == 'present'
assert p.address == '1.1.1.1' assert p.address == '1.1.1.1'
assert p.netmask == '2.2.2.2' assert p.netmask == '2.2.2.2'
@ -83,7 +85,7 @@ class TestParameters(unittest.TestCase):
def test_api_parameters(self): def test_api_parameters(self):
args = load_fixture('load_ltm_virtual_address_default.json') args = load_fixture('load_ltm_virtual_address_default.json')
p = Parameters(args) p = Parameters(params=args)
assert p.name == '1.1.1.1' assert p.name == '1.1.1.1'
assert p.address == '1.1.1.1' assert p.address == '1.1.1.1'
assert p.arp_state == 'enabled' assert p.arp_state == 'enabled'
@ -99,56 +101,56 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
advertise_route='when_all_available' advertise_route='when_all_available'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.advertise_route == 'all' assert p.advertise_route == 'all'
def test_module_parameters_advertise_route_any(self): def test_module_parameters_advertise_route_any(self):
args = dict( args = dict(
advertise_route='when_any_available' advertise_route='when_any_available'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.advertise_route == 'any' assert p.advertise_route == 'any'
def test_module_parameters_icmp_echo_disabled(self): def test_module_parameters_icmp_echo_disabled(self):
args = dict( args = dict(
icmp_echo='disabled' icmp_echo='disabled'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.icmp_echo == 'disabled' assert p.icmp_echo == 'disabled'
def test_module_parameters_icmp_echo_selective(self): def test_module_parameters_icmp_echo_selective(self):
args = dict( args = dict(
icmp_echo='selective' icmp_echo='selective'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.icmp_echo == 'selective' assert p.icmp_echo == 'selective'
def test_module_parameters_auto_delete_disabled(self): def test_module_parameters_auto_delete_disabled(self):
args = dict( args = dict(
auto_delete='disabled' auto_delete='disabled'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.auto_delete is False assert p.auto_delete is False
def test_module_parameters_arp_state_disabled(self): def test_module_parameters_arp_state_disabled(self):
args = dict( args = dict(
arp_state='disabled' arp_state='disabled'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.arp_state == 'disabled' assert p.arp_state == 'disabled'
def test_module_parameters_use_route_advert_disabled(self): def test_module_parameters_use_route_advert_disabled(self):
args = dict( args = dict(
use_route_advertisement='no' use_route_advertisement='no'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.use_route_advertisement == 'disabled' assert p.use_route_advertisement == 'disabled'
def test_module_parameters_state_present(self): def test_module_parameters_state_present(self):
args = dict( args = dict(
state='present' state='present'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.state == 'present' assert p.state == 'present'
assert p.enabled == 'yes' assert p.enabled == 'yes'
@ -156,14 +158,14 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
state='absent' state='absent'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.state == 'absent' assert p.state == 'absent'
def test_module_parameters_state_enabled(self): def test_module_parameters_state_enabled(self):
args = dict( args = dict(
state='enabled' state='enabled'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.state == 'enabled' assert p.state == 'enabled'
assert p.enabled == 'yes' assert p.enabled == 'yes'
@ -171,13 +173,11 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
state='disabled' state='disabled'
) )
p = Parameters(args) p = Parameters(params=args)
assert p.state == 'disabled' assert p.state == 'disabled'
assert p.enabled == 'no' assert p.enabled == 'no'
@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):
@ -199,12 +199,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])
@ -222,12 +221,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=[True, False]) mm.exists = Mock(side_effect=[True, False])

@ -17,28 +17,24 @@ 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_virtual_server import VirtualAddressParameters from library.bigip_virtual_server import ModuleParameters
from library.bigip_virtual_server import VirtualServerModuleParameters from library.bigip_virtual_server import ApiParameters
from library.bigip_virtual_server import VirtualServerApiParameters
from library.bigip_virtual_server import ModuleManager from library.bigip_virtual_server import ModuleManager
from library.bigip_virtual_server import VirtualServerManager
from library.bigip_virtual_server import VirtualAddressManager
from library.bigip_virtual_server import ArgumentSpec from library.bigip_virtual_server 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_virtual_server import VirtualAddressParameters from ansible.modules.network.f5.bigip_virtual_server import ApiParameters
from ansible.modules.network.f5.bigip_virtual_server import VirtualServerApiParameters from ansible.modules.network.f5.bigip_virtual_server import ModuleParameters
from ansible.modules.network.f5.bigip_virtual_server import VirtualServerModuleParameters
from ansible.modules.network.f5.bigip_virtual_server import ModuleManager from ansible.modules.network.f5.bigip_virtual_server import ModuleManager
from ansible.modules.network.f5.bigip_virtual_server import VirtualServerManager
from ansible.modules.network.f5.bigip_virtual_server import VirtualAddressManager
from ansible.modules.network.f5.bigip_virtual_server import ArgumentSpec from ansible.modules.network.f5.bigip_virtual_server 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")
@ -70,14 +66,14 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
destination='1.1.1.1' destination='1.1.1.1'
) )
p = VirtualServerApiParameters(args) p = ApiParameters(params=args)
assert p.destination_tuple.ip == '1.1.1.1' assert p.destination_tuple.ip == '1.1.1.1'
def test_destination_mutex_2(self): def test_destination_mutex_2(self):
args = dict( args = dict(
destination='1.1.1.1%2' destination='1.1.1.1%2'
) )
p = VirtualServerApiParameters(args) p = ApiParameters(params=args)
assert p.destination_tuple.ip == '1.1.1.1' assert p.destination_tuple.ip == '1.1.1.1'
assert p.destination_tuple.route_domain == 2 assert p.destination_tuple.route_domain == 2
@ -85,7 +81,7 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
destination='1.1.1.1:80' destination='1.1.1.1:80'
) )
p = VirtualServerApiParameters(args) p = ApiParameters(params=args)
assert p.destination_tuple.ip == '1.1.1.1' assert p.destination_tuple.ip == '1.1.1.1'
assert p.destination_tuple.port == 80 assert p.destination_tuple.port == 80
@ -93,7 +89,7 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
destination='1.1.1.1%2:80' destination='1.1.1.1%2:80'
) )
p = VirtualServerApiParameters(args) p = ApiParameters(params=args)
assert p.destination_tuple.ip == '1.1.1.1' assert p.destination_tuple.ip == '1.1.1.1'
assert p.destination_tuple.port == 80 assert p.destination_tuple.port == 80
assert p.destination_tuple.route_domain == 2 assert p.destination_tuple.route_domain == 2
@ -102,14 +98,14 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
destination='/Common/1.1.1.1' destination='/Common/1.1.1.1'
) )
p = VirtualServerApiParameters(args) p = ApiParameters(params=args)
assert p.destination_tuple.ip == '1.1.1.1' assert p.destination_tuple.ip == '1.1.1.1'
def test_api_destination_mutex_6(self): def test_api_destination_mutex_6(self):
args = dict( args = dict(
destination='/Common/1.1.1.1%2' destination='/Common/1.1.1.1%2'
) )
p = VirtualServerApiParameters(args) p = ApiParameters(params=args)
assert p.destination_tuple.ip == '1.1.1.1' assert p.destination_tuple.ip == '1.1.1.1'
assert p.destination_tuple.route_domain == 2 assert p.destination_tuple.route_domain == 2
@ -117,7 +113,7 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
destination='/Common/1.1.1.1:80' destination='/Common/1.1.1.1:80'
) )
p = VirtualServerApiParameters(args) p = ApiParameters(params=args)
assert p.destination_tuple.ip == '1.1.1.1' assert p.destination_tuple.ip == '1.1.1.1'
assert p.destination_tuple.port == 80 assert p.destination_tuple.port == 80
@ -125,7 +121,7 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
destination='/Common/1.1.1.1%2:80' destination='/Common/1.1.1.1%2:80'
) )
p = VirtualServerApiParameters(args) p = ApiParameters(params=args)
assert p.destination_tuple.ip == '1.1.1.1' assert p.destination_tuple.ip == '1.1.1.1'
assert p.destination_tuple.port == 80 assert p.destination_tuple.port == 80
assert p.destination_tuple.route_domain == 2 assert p.destination_tuple.route_domain == 2
@ -134,14 +130,14 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
destination='2700:bc00:1f10:101::6' destination='2700:bc00:1f10:101::6'
) )
p = VirtualServerApiParameters(args) p = ApiParameters(params=args)
assert p.destination_tuple.ip == '2700:bc00:1f10:101::6' assert p.destination_tuple.ip == '2700:bc00:1f10:101::6'
def test_destination_mutex_10(self): def test_destination_mutex_10(self):
args = dict( args = dict(
destination='2700:bc00:1f10:101::6%2' destination='2700:bc00:1f10:101::6%2'
) )
p = VirtualServerApiParameters(args) p = ApiParameters(params=args)
assert p.destination_tuple.ip == '2700:bc00:1f10:101::6' assert p.destination_tuple.ip == '2700:bc00:1f10:101::6'
assert p.destination_tuple.route_domain == 2 assert p.destination_tuple.route_domain == 2
@ -149,7 +145,7 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
destination='2700:bc00:1f10:101::6.80' destination='2700:bc00:1f10:101::6.80'
) )
p = VirtualServerApiParameters(args) p = ApiParameters(params=args)
assert p.destination_tuple.ip == '2700:bc00:1f10:101::6' assert p.destination_tuple.ip == '2700:bc00:1f10:101::6'
assert p.destination_tuple.port == 80 assert p.destination_tuple.port == 80
@ -157,26 +153,11 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
destination='2700:bc00:1f10:101::6%2.80' destination='2700:bc00:1f10:101::6%2.80'
) )
p = VirtualServerApiParameters(args) p = ApiParameters(params=args)
assert p.destination_tuple.ip == '2700:bc00:1f10:101::6' assert p.destination_tuple.ip == '2700:bc00:1f10:101::6'
assert p.destination_tuple.port == 80 assert p.destination_tuple.port == 80
assert p.destination_tuple.route_domain == 2 assert p.destination_tuple.route_domain == 2
#
# def test_destination_mutex_6(self):
# args = dict(
# destination='/Common/2700:bc00:1f10:101::6'
# )
# p = VirtualServerParameters(args)
# assert p.destination_tuple.ip == '2700:bc00:1f10:101::6'
#
# def test_destination_mutex_5(self):
# args = dict(
# destination='/Common/2700:bc00:1f10:101::6'
# )
# p = VirtualServerParameters(args)
# assert p.destination_tuple.ip == '2700:bc00:1f10:101::6'
def test_module_no_partition_prefix_parameters(self): def test_module_no_partition_prefix_parameters(self):
args = dict( args = dict(
server='localhost', server='localhost',
@ -198,7 +179,7 @@ class TestParameters(unittest.TestCase):
], ],
enabled_vlans=['vlan2'] enabled_vlans=['vlan2']
) )
p = VirtualServerModuleParameters(args) p = ModuleParameters(params=args)
assert p.name == 'my-virtual-server' assert p.name == 'my-virtual-server'
assert p.partition == 'Common' assert p.partition == 'Common'
assert p.port == 443 assert p.port == 443
@ -235,7 +216,7 @@ class TestParameters(unittest.TestCase):
], ],
enabled_vlans=['/Common/vlan2'] enabled_vlans=['/Common/vlan2']
) )
p = VirtualServerModuleParameters(args) p = ModuleParameters(params=args)
assert p.name == 'my-virtual-server' assert p.name == 'my-virtual-server'
assert p.partition == 'Common' assert p.partition == 'Common'
assert p.port == 443 assert p.port == 443
@ -342,7 +323,7 @@ class TestParameters(unittest.TestCase):
] ]
} }
} }
p = VirtualServerApiParameters(args) p = ApiParameters(params=args)
assert p.name == 'my-virtual-server' assert p.name == 'my-virtual-server'
assert p.partition == 'Common' assert p.partition == 'Common'
assert p.port == 443 assert p.port == 443
@ -358,8 +339,6 @@ class TestParameters(unittest.TestCase):
assert '/Common/net1' in p.vlans assert '/Common/net1' in p.vlans
@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):
@ -388,19 +367,15 @@ class TestManager(unittest.TestCase):
validate_certs="no" validate_certs="no"
)) ))
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
vsm = VirtualServerManager(client) mm = ModuleManager(module=module)
vsm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)
vsm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)
mm = ModuleManager(client)
mm.get_manager = Mock(return_value=vsm)
results = mm.exec_module() results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
@ -423,18 +398,14 @@ class TestManager(unittest.TestCase):
validate_certs="no" validate_certs="no"
)) ))
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
vsm = VirtualServerManager(client) mm = ModuleManager(module=module)
vsm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)
mm = ModuleManager(client)
mm.get_manager = Mock(return_value=vsm)
results = mm.exec_module() results = mm.exec_module()
@ -460,26 +431,22 @@ 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 = VirtualServerApiParameters( current = ApiParameters(
dict( dict(
agent_status_traps='disabled' agent_status_traps='disabled'
) )
) )
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
vsm = VirtualServerManager(client) mm = ModuleManager(module=module)
vsm.exists = Mock(return_value=False) mm.exists = Mock(return_value=False)
vsm.update_on_device = Mock(return_value=True) mm.update_on_device = Mock(return_value=True)
vsm.read_current_from_device = Mock(return_value=current) mm.read_current_from_device = Mock(return_value=current)
mm = ModuleManager(client)
mm.get_manager = Mock(return_value=vsm)
results = mm.exec_module() results = mm.exec_module()
assert results['changed'] is False assert results['changed'] is False
@ -498,22 +465,18 @@ 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 = VirtualServerApiParameters(load_fixture('load_ltm_virtual_1.json')) current = ApiParameters(params=load_fixture('load_ltm_virtual_1.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 to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
vsm = VirtualServerManager(client) mm = ModuleManager(module=module)
vsm.exists = Mock(return_value=True) mm.exists = Mock(return_value=True)
vsm.read_current_from_device = Mock(return_value=current) mm.read_current_from_device = Mock(return_value=current)
vsm.update_on_device = Mock(return_value=True) mm.update_on_device = Mock(return_value=True)
mm = ModuleManager(client)
mm.get_manager = Mock(return_value=vsm)
results = mm.exec_module() results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
@ -532,21 +495,17 @@ 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 = VirtualServerApiParameters(load_fixture('load_ltm_virtual_1.json')) current = ApiParameters(params=load_fixture('load_ltm_virtual_1.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 to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
vsm = VirtualServerManager(client) mm = ModuleManager(module=module)
vsm.exists = Mock(return_value=True) mm.exists = Mock(return_value=True)
vsm.read_current_from_device = Mock(return_value=current) mm.read_current_from_device = Mock(return_value=current)
mm = ModuleManager(client)
mm.get_manager = Mock(return_value=vsm)
results = mm.exec_module() results = mm.exec_module()
assert results['changed'] is False assert results['changed'] is False
@ -567,21 +526,17 @@ 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 = VirtualServerApiParameters(load_fixture('load_ltm_virtual_2.json')) current = ApiParameters(params=load_fixture('load_ltm_virtual_2.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 to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
vsm = VirtualServerManager(client) mm = ModuleManager(module=module)
vsm.exists = Mock(return_value=True) mm.exists = Mock(return_value=True)
vsm.read_current_from_device = Mock(return_value=current) mm.read_current_from_device = Mock(return_value=current)
mm = ModuleManager(client)
mm.get_manager = Mock(return_value=vsm)
results = mm.exec_module() results = mm.exec_module()
@ -603,22 +558,18 @@ 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 = VirtualServerApiParameters(load_fixture('load_ltm_virtual_2.json')) current = ApiParameters(params=load_fixture('load_ltm_virtual_2.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 to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
vsm = VirtualServerManager(client) mm = ModuleManager(module=module)
vsm.exists = Mock(return_value=True) mm.exists = Mock(return_value=True)
vsm.read_current_from_device = Mock(return_value=current) mm.read_current_from_device = Mock(return_value=current)
vsm.update_on_device = Mock(return_value=True) mm.update_on_device = Mock(return_value=True)
mm = ModuleManager(client)
mm.get_manager = Mock(return_value=vsm)
results = mm.exec_module() results = mm.exec_module()
@ -674,22 +625,18 @@ 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 = VirtualServerApiParameters(load_fixture('load_ltm_virtual_3.json')) current = ApiParameters(params=load_fixture('load_ltm_virtual_3.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 to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
vsm = VirtualServerManager(client) mm = ModuleManager(module=module)
vsm.exists = Mock(return_value=True) mm.exists = Mock(return_value=True)
vsm.read_current_from_device = Mock(return_value=current) mm.read_current_from_device = Mock(return_value=current)
vsm.update_on_device = Mock(return_value=True) mm.update_on_device = Mock(return_value=True)
mm = ModuleManager(client)
mm.get_manager = Mock(return_value=vsm)
results = mm.exec_module() results = mm.exec_module()
@ -727,47 +674,3 @@ class TestManager(unittest.TestCase):
assert 'context' in results['profiles'][1] assert 'context' in results['profiles'][1]
assert results['profiles'][1]['name'] == 'clientssl' assert results['profiles'][1]['name'] == 'clientssl'
assert results['profiles'][1]['context'] == 'clientside' assert results['profiles'][1]['context'] == 'clientside'
@patch('ansible.module_utils.f5_utils.AnsibleF5Client._get_mgmt_root',
return_value=True)
class TestDeprecatedAnsible24Manager(unittest.TestCase):
def setUp(self):
self.spec = ArgumentSpec()
def test_modify_port_idempotent(self, *args):
set_module_args(dict(
destination="10.10.10.10",
name="my-virtual-server",
route_advertisement_state="enabled",
partition="Common",
password="secret",
port="443",
server="localhost",
state="present",
user="admin",
validate_certs="no"
))
client = AnsibleF5Client(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode,
f5_product_name=self.spec.f5_product_name
)
vsm_current = VirtualServerApiParameters(load_fixture('load_ltm_virtual_1.json'))
vam_current = VirtualAddressParameters(load_fixture('load_ltm_virtual_1_address.json'))
vsm = VirtualServerManager(client)
vsm.exists = Mock(return_value=True)
vsm.read_current_from_device = Mock(return_value=vsm_current)
vam = VirtualAddressManager(client)
vam.exists = Mock(return_value=True)
vam.read_current_from_device = Mock(return_value=vam_current)
mm = ModuleManager(client)
mm.get_manager = Mock(side_effect=[vsm, vam])
results = mm.exec_module()
assert results['changed'] is False

@ -8,7 +8,6 @@ __metaclass__ = type
import os import os
import json import json
import pytest
import sys import sys
from nose.plugins.skip import SkipTest from nose.plugins.skip import SkipTest
@ -18,21 +17,24 @@ 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_vlan import Parameters from library.bigip_vlan import ApiParameters
from library.bigip_vlan import ModuleParameters
from library.bigip_vlan import ModuleManager from library.bigip_vlan import ModuleManager
from library.bigip_vlan import ArgumentSpec from library.bigip_vlan 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_vlan import Parameters from ansible.modules.network.f5.bigip_vlan import ApiParameters
from ansible.modules.network.f5.bigip_vlan import ModuleParameters
from ansible.modules.network.f5.bigip_vlan import ModuleManager from ansible.modules.network.f5.bigip_vlan import ModuleManager
from ansible.modules.network.f5.bigip_vlan import ArgumentSpec from ansible.modules.network.f5.bigip_vlan 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,13 +67,6 @@ class BigIpObj(object):
class TestParameters(unittest.TestCase): class TestParameters(unittest.TestCase):
def setUp(self):
self.loaded_ifcs = []
ifcs_json = load_fixture('load_net_interfaces.json')
for item in ifcs_json:
self.loaded_ifcs.append(BigIpObj(**item))
def test_module_parameters(self): def test_module_parameters(self):
args = dict( args = dict(
name='somevlan', name='somevlan',
@ -79,9 +74,7 @@ class TestParameters(unittest.TestCase):
description='fakevlan', description='fakevlan',
untagged_interfaces=['1.1'], untagged_interfaces=['1.1'],
) )
with patch.object(Parameters, '_get_interfaces_from_device') as obj: p = ModuleParameters(params=args)
obj.return_value = self.loaded_ifcs
p = Parameters(args)
assert p.name == 'somevlan' assert p.name == 'somevlan'
assert p.tag == 213 assert p.tag == 213
@ -92,38 +85,20 @@ class TestParameters(unittest.TestCase):
args = dict( args = dict(
name='somevlan', name='somevlan',
description='fakevlan', description='fakevlan',
tag=213, tag=213
tagged_interfaces=['1.2']
) )
with patch.object(Parameters, '_get_interfaces_from_device') as obj: p = ApiParameters(params=args)
obj.return_value = self.loaded_ifcs
p = Parameters(args)
assert p.name == 'somevlan' assert p.name == 'somevlan'
assert p.tag == 213 assert p.tag == 213
assert p.interfaces == [{'tagged': True, 'name': '1.2'}]
assert p.description == 'fakevlan' assert p.description == 'fakevlan'
@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):
self.spec = ArgumentSpec() self.spec = ArgumentSpec()
self.loaded_ifcs = []
self.loaded_vlan_ifc_tag = []
self.loaded_vlan_ifc_untag = []
ifcs_tag = load_fixture('load_vlan_tagged_ifcs.json')
ifcs_untag = load_fixture('load_vlan_untag_ifcs.json')
ifcs_json = load_fixture('load_net_interfaces.json')
for item in ifcs_json:
self.loaded_ifcs.append(BigIpObj(**item))
for item in ifcs_tag:
self.loaded_vlan_ifc_tag.append(BigIpObj(**item))
for item in ifcs_untag:
self.loaded_vlan_ifc_untag.append(BigIpObj(**item))
def test_create_vlan(self, *args): def test_create_vlan(self, *args):
set_module_args(dict( set_module_args(dict(
@ -135,24 +110,19 @@ class TestManager(unittest.TestCase):
partition='Common' partition='Common'
)) ))
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
with patch.object(Parameters, '_get_interfaces_from_device') as obj: mm = ModuleManager(module=module)
obj.return_value = self.loaded_ifcs mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False)
mm = ModuleManager(client) results = mm.exec_module()
mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['name'] == 'somevlan'
assert results['description'] == 'fakevlan' assert results['description'] == 'fakevlan'
def test_create_vlan_tagged_interface(self, *args): def test_create_vlan_tagged_interface(self, *args):
@ -166,26 +136,21 @@ class TestManager(unittest.TestCase):
partition='Common' partition='Common'
)) ))
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
with patch.object(Parameters, '_get_interfaces_from_device') as obj: mm = ModuleManager(module=module)
obj.return_value = self.loaded_ifcs mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False)
mm = ModuleManager(client)
mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False)
results = mm.exec_module() results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['interfaces'] == [{'tagged': True, 'name': '2.1'}] assert results['tagged_interfaces'] == ['2.1']
assert results['tag'] == 213 assert results['tag'] == 213
assert results['name'] == 'somevlan'
def test_create_vlan_untagged_interface(self, *args): def test_create_vlan_untagged_interface(self, *args):
set_module_args(dict( set_module_args(dict(
@ -197,25 +162,20 @@ class TestManager(unittest.TestCase):
partition='Common' partition='Common'
)) ))
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
with patch.object(Parameters, '_get_interfaces_from_device') as obj: mm = ModuleManager(module=module)
obj.return_value = self.loaded_ifcs mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False)
mm = ModuleManager(client) results = mm.exec_module()
mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['interfaces'] == [{'untagged': True, 'name': '2.1'}] assert results['untagged_interfaces'] == ['2.1']
assert results['name'] == 'somevlan'
def test_create_vlan_tagged_interfaces(self, *args): def test_create_vlan_tagged_interfaces(self, *args):
set_module_args(dict( set_module_args(dict(
@ -228,27 +188,21 @@ class TestManager(unittest.TestCase):
partition='Common' partition='Common'
)) ))
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
with patch.object(Parameters, '_get_interfaces_from_device') as obj: mm = ModuleManager(module=module)
obj.return_value = self.loaded_ifcs mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False)
mm = ModuleManager(client)
mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False)
results = mm.exec_module() results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['interfaces'] == [{'tagged': True, 'name': '2.1'}, assert results['tagged_interfaces'] == ['1.1', '2.1']
{'tagged': True, 'name': '1.1'}]
assert results['tag'] == 213 assert results['tag'] == 213
assert results['name'] == 'somevlan'
def test_create_vlan_untagged_interfaces(self, *args): def test_create_vlan_untagged_interfaces(self, *args):
set_module_args(dict( set_module_args(dict(
@ -260,26 +214,20 @@ class TestManager(unittest.TestCase):
partition='Common', partition='Common',
)) ))
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
with patch.object(Parameters, '_get_interfaces_from_device') as obj: mm = ModuleManager(module=module)
obj.return_value = self.loaded_ifcs mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False)
mm = ModuleManager(client) results = mm.exec_module()
mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False)
results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['interfaces'] == [{'untagged': True, 'name': '2.1'}, assert results['untagged_interfaces'] == ['1.1', '2.1']
{'untagged': True, 'name': '1.1'}]
assert results['name'] == 'somevlan'
def test_update_vlan_untag_interface(self, *args): def test_update_vlan_untag_interface(self, *args):
set_module_args(dict( set_module_args(dict(
@ -291,32 +239,26 @@ class TestManager(unittest.TestCase):
partition='Common', partition='Common',
)) ))
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
) )
ifcs = self.loaded_vlan_ifc_untag
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
with patch.object(Parameters, '_get_interfaces_from_device') as obj: mm = ModuleManager(module=module)
obj.return_value = self.loaded_ifcs
mm = ModuleManager(client)
current = ( current = ApiParameters(params=load_fixture('load_vlan.json'))
Parameters( interfaces = load_fixture('load_vlan_interfaces.json')
load_fixture('load_vlan.json') current.update({'interfaces': interfaces})
),
ifcs
)
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)
mm.read_current_from_device = Mock(return_value=current) mm.read_current_from_device = Mock(return_value=current)
results = mm.exec_module() results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['interfaces'] == [{'untagged': True, 'name': '2.1'}] assert results['untagged_interfaces'] == ['2.1']
def test_update_vlan_tag_interface(self, *args): def test_update_vlan_tag_interface(self, *args):
set_module_args(dict( set_module_args(dict(
@ -328,32 +270,24 @@ class TestManager(unittest.TestCase):
partition='Common', partition='Common',
)) ))
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
) )
ifcs = self.loaded_vlan_ifc_tag
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
with patch.object(Parameters, '_get_interfaces_from_device') as obj: mm = ModuleManager(module=module)
obj.return_value = self.loaded_ifcs
mm = ModuleManager(client)
current = ( current = ApiParameters(params=load_fixture('load_vlan.json'))
Parameters(
load_fixture('load_vlan.json')
),
ifcs
)
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)
mm.read_current_from_device = Mock(return_value=current) mm.read_current_from_device = Mock(return_value=current)
results = mm.exec_module() results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['interfaces'] == [{'tagged': True, 'name': '2.1'}] assert results['tagged_interfaces'] == ['2.1']
def test_update_vlan_description(self, *args): def test_update_vlan_description(self, *args):
set_module_args(dict( set_module_args(dict(
@ -365,117 +299,21 @@ class TestManager(unittest.TestCase):
partition='Common', partition='Common',
)) ))
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
) )
ifcs = self.loaded_vlan_ifc_tag
# Override methods to force specific logic in the module to happen # Override methods to force specific logic in the module to happen
with patch.object(Parameters, '_get_interfaces_from_device') as obj: mm = ModuleManager(module=module)
obj.return_value = self.loaded_ifcs
mm = ModuleManager(client)
current = ( current = ApiParameters(params=load_fixture('update_vlan_description.json'))
Parameters(
load_fixture('update_vlan_description.json')
),
ifcs
)
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)
mm.read_current_from_device = Mock(return_value=current) mm.read_current_from_device = Mock(return_value=current)
results = mm.exec_module() results = mm.exec_module()
assert results['changed'] is True assert results['changed'] is True
assert results['description'] == 'changed_that' assert results['description'] == 'changed_that'
def test_untagged_ifc_raises(self, *args):
set_module_args(dict(
name='somevlan',
untagged_interface=['10.2'],
server='localhost',
password='password',
user='admin',
partition='Common'
))
client = AnsibleF5Client(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode,
f5_product_name=self.spec.f5_product_name
)
msg = 'The specified interface "10.2" was not found'
# Override methods to force specific logic in the module to happen
with patch.object(Parameters, '_get_interfaces_from_device') as obj:
obj.return_value = self.loaded_ifcs
mm = ModuleManager(client)
mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False)
with pytest.raises(F5ModuleError) as err:
mm.exec_module()
assert str(err.value) == msg
def test_tagged_ifc_raises(self, *args):
set_module_args(dict(
name='somevlan',
tagged_interface=['10.2'],
tag=213,
server='localhost',
password='password',
user='admin',
partition='Common'
))
client = AnsibleF5Client(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode,
f5_product_name=self.spec.f5_product_name
)
msg = 'The specified interface "10.2" was not found'
# Override methods to force specific logic in the module to happen
with patch.object(Parameters, '_get_interfaces_from_device') as obj:
obj.return_value = self.loaded_ifcs
mm = ModuleManager(client)
mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False)
with pytest.raises(F5ModuleError) as err:
mm.exec_module()
assert str(err.value) == msg
def test_parse_return_ifcs_raises(self, *args):
set_module_args(dict(
name='somevlan',
untagged_interface=['1.2'],
server='localhost',
password='password',
user='admin',
partition='Common'
))
client = AnsibleF5Client(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode,
f5_product_name=self.spec.f5_product_name
)
msg = 'No interfaces were found'
# Override methods to force specific logic in the module to happen
with patch.object(Parameters, '_get_interfaces_from_device') as obj:
obj.return_value = []
mm = ModuleManager(client)
mm.create_on_device = Mock(return_value=True)
mm.exists = Mock(return_value=False)
with pytest.raises(F5ModuleError) as err:
mm.exec_module()
assert str(err.value) == msg

@ -18,23 +18,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
from ansible.module_utils.f5_utils import F5ModuleError
try: try:
from library.bigip_wait import Parameters from library.bigip_wait import Parameters
from library.bigip_wait import ModuleManager from library.bigip_wait import ModuleManager
from library.bigip_wait import ArgumentSpec from library.bigip_wait import ArgumentSpec
from library.bigip_wait import AnsibleF5ClientStub 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_wait import Parameters from ansible.modules.network.f5.bigip_wait import Parameters
from ansible.modules.network.f5.bigip_wait import ModuleManager from ansible.modules.network.f5.bigip_wait import ModuleManager
from ansible.modules.network.f5.bigip_wait import ArgumentSpec from ansible.modules.network.f5.bigip_wait import ArgumentSpec
from ansible.modules.network.f5.bigip_wait import AnsibleF5ClientStub 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")
@ -70,7 +69,7 @@ class TestParameters(unittest.TestCase):
msg='We timed out during waiting for BIG-IP :-(' msg='We timed out during waiting for BIG-IP :-('
) )
p = Parameters(args) p = Parameters(params=args)
assert p.delay == 3 assert p.delay == 3
assert p.timeout == 500 assert p.timeout == 500
assert p.sleep == 10 assert p.sleep == 10
@ -84,15 +83,13 @@ class TestParameters(unittest.TestCase):
msg='We timed out during waiting for BIG-IP :-(' msg='We timed out during waiting for BIG-IP :-('
) )
p = Parameters(args) p = Parameters(params=args)
assert p.delay == 3 assert p.delay == 3
assert p.timeout == 500 assert p.timeout == 500
assert p.sleep == 10 assert p.sleep == 10
assert p.msg == 'We timed out during waiting for BIG-IP :-(' assert p.msg == 'We timed out during waiting for BIG-IP :-('
@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):
self.spec = ArgumentSpec() self.spec = ArgumentSpec()
@ -104,14 +101,13 @@ class TestManager(unittest.TestCase):
user='admin' user='admin'
)) ))
client = AnsibleF5ClientStub( 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._connect_to_device = Mock(return_value=True) mm._connect_to_device = Mock(return_value=True)
mm._device_is_rebooting = Mock(return_value=False) mm._device_is_rebooting = Mock(return_value=False)
mm._is_mprov_running_on_device = Mock(return_value=False) mm._is_mprov_running_on_device = Mock(return_value=False)

@ -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.bigiq_regkey_license import ModuleParameters from library.bigiq_regkey_license import ModuleParameters
from library.bigiq_regkey_license import ApiParameters from library.bigiq_regkey_license import ApiParameters
from library.bigiq_regkey_license import ModuleManager from library.bigiq_regkey_license import ModuleManager
from library.bigiq_regkey_license import ArgumentSpec from library.bigiq_regkey_license 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.bigiq_regkey_license import ApiParameters from ansible.modules.network.f5.bigiq_regkey_license import ApiParameters
from ansible.modules.network.f5.bigiq_regkey_license import ModuleManager from ansible.modules.network.f5.bigiq_regkey_license import ModuleManager
from ansible.modules.network.f5.bigiq_regkey_license import ArgumentSpec from ansible.modules.network.f5.bigiq_regkey_license 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")
@ -70,7 +71,7 @@ class TestParameters(unittest.TestCase):
description='this is a description' description='this is a description'
) )
p = ModuleParameters(args) p = ModuleParameters(params=args)
assert p.regkey_pool == 'foo' assert p.regkey_pool == 'foo'
assert p.license_key == 'XXXX-XXXX-XXXX-XXXX-XXXX' assert p.license_key == 'XXXX-XXXX-XXXX-XXXX-XXXX'
assert p.accept_eula is True assert p.accept_eula is True
@ -79,12 +80,10 @@ class TestParameters(unittest.TestCase):
def test_api_parameters(self): def test_api_parameters(self):
args = load_fixture('load_regkey_license_key.json') args = load_fixture('load_regkey_license_key.json')
p = ApiParameters(args) p = ApiParameters(params=args)
assert p.description == 'foo bar baz' assert p.description == 'foo bar baz'
@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):
@ -101,12 +100,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])

@ -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
from ansible.module_utils.f5_utils import F5ModuleError
try: try:
from library.bigiq_regkey_pool import ModuleParameters from library.bigiq_regkey_pool import ModuleParameters
from library.bigiq_regkey_pool import ApiParameters from library.bigiq_regkey_pool import ApiParameters
from library.bigiq_regkey_pool import ModuleManager from library.bigiq_regkey_pool import ModuleManager
from library.bigiq_regkey_pool import ArgumentSpec from library.bigiq_regkey_pool import ArgumentSpec
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,6 +34,8 @@ except ImportError:
from ansible.modules.network.f5.bigiq_regkey_pool import ApiParameters from ansible.modules.network.f5.bigiq_regkey_pool import ApiParameters
from ansible.modules.network.f5.bigiq_regkey_pool import ModuleManager from ansible.modules.network.f5.bigiq_regkey_pool import ModuleManager
from ansible.modules.network.f5.bigiq_regkey_pool import ArgumentSpec from ansible.modules.network.f5.bigiq_regkey_pool import ArgumentSpec
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,18 +68,16 @@ class TestParameters(unittest.TestCase):
description='this is a description' description='this is a description'
) )
p = ModuleParameters(args) p = ModuleParameters(params=args)
assert p.description == 'this is a description' assert p.description == 'this is a description'
def test_api_parameters(self): def test_api_parameters(self):
args = load_fixture('load_regkey_license_pool.json') args = load_fixture('load_regkey_license_pool.json')
p = ApiParameters(args) p = ApiParameters(params=args)
assert p.description == 'this is a description' assert p.description == 'this is a description'
@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):
@ -91,14 +92,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(return_value=False) mm.exists = Mock(return_value=False)
mm.create_on_device = Mock(return_value=True) mm.create_on_device = Mock(return_value=True)

Loading…
Cancel
Save