Various fixes to F5 modules (#39255)

* Adds gnat provisioning to bigip_provision
* Adds special handling for AFM in bigip_provision
* Add device rebooting for provisioning as necessary
* Refactored route domain module to be inline with current f5 conventions
* Minor refactors across modules
This commit is contained in:
Tim Rupp 2018-04-24 19:49:52 -07:00 committed by GitHub
parent 39ca41eb1b
commit 256b5535ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 845 additions and 481 deletions

View file

@ -19,9 +19,9 @@ short_description: Manage BIG-IP module provisioning
description: description:
- Manage BIG-IP module provisioning. This module will only provision at the - Manage BIG-IP module provisioning. This module will only provision at the
standard levels of Dedicated, Nominal, and Minimum. standard levels of Dedicated, Nominal, and Minimum.
version_added: "2.4" version_added: 2.4
options: options:
name: module:
description: description:
- The module to provision in BIG-IP. - The module to provision in BIG-IP.
required: true required: true
@ -31,6 +31,7 @@ options:
- apm - apm
- asm - asm
- avr - avr
- cgnat
- fps - fps
- gtm - gtm
- ilx - ilx
@ -41,7 +42,7 @@ options:
- swg - swg
- vcmp - vcmp
aliases: aliases:
- module - name
level: level:
description: description:
- Sets the provisioning level for the requested modules. Changing the - Sets the provisioning level for the requested modules. Changing the
@ -49,6 +50,8 @@ options:
For example, changing one module to C(dedicated) requires setting all For example, changing one module to C(dedicated) requires setting all
others to C(none). Setting the level of a module to C(none) means that others to C(none). Setting the level of a module to C(none) means that
the module is not activated. the module is not activated.
- This parameter is not relevant to C(cgnat) and will not be applied to the
C(cgnat) module.
default: nominal default: nominal
choices: choices:
- dedicated - dedicated
@ -104,49 +107,40 @@ import time
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
HAS_DEVEL_IMPORTS = False
try: try:
# Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK 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.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError 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 AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens 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 from library.module_utils.network.f5.common import f5_argument_spec
try: try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
from f5.bigip.contexts import TransactionContextManager
from f5.sdk_exception import LazyAttributesRequired
except ImportError: except ImportError:
HAS_F5SDK = False 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 HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client 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 F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters 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 cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec from ansible.module_utils.network.f5.common import f5_argument_spec
try: try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
from f5.bigip.contexts import TransactionContextManager
from f5.sdk_exception import LazyAttributesRequired
except ImportError: except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
try:
from f5.bigip.contexts import TransactionContextManager
from f5.sdk_exception import LazyAttributesRequired
except ImportError:
HAS_F5SDK = False
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
api_attributes = ['level'] api_attributes = ['level']
returnables = ['level'] returnables = ['level']
updatables = ['level'] updatables = ['level', 'cgnat']
def to_return(self): def to_return(self):
result = {} result = {}
@ -162,27 +156,85 @@ class Parameters(AnsibleF5Parameters):
def level(self): def level(self):
if self._values['level'] is None: if self._values['level'] is None:
return None return None
if self.state == 'absent':
return 'none'
return str(self._values['level']) return str(self._values['level'])
class ApiParameters(Parameters):
pass
class ModuleParameters(Parameters):
pass
class Changes(Parameters):
pass
class UsableChanges(Parameters):
pass
class ReportableChanges(Parameters):
pass
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:
result = self.__default(param)
return result
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 cgnat(self):
if self.want.module == 'cgnat':
if self.want.state == 'absent' and self.have.enabled is True:
return True
if self.want.state == 'present' and self.have.disabled is True:
return True
class ModuleManager(object): class ModuleManager(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.module = kwargs.get('module', None) self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None) self.client = kwargs.get('client', None)
self.have = None self.have = None
self.want = Parameters(params=self.module.params) self.want = ModuleParameters(params=self.module.params)
self.changes = Parameters() self.changes = UsableChanges()
def _update_changed_options(self): def _update_changed_options(self):
changed = {} diff = Difference(self.want, self.have)
for key in Parameters.updatables: updatables = Parameters.updatables
if getattr(self.want, key) is not None: changed = dict()
attr1 = getattr(self.want, key) for k in updatables:
attr2 = getattr(self.have, key) change = diff.compare(k)
if attr1 != attr2: if change is None:
changed[key] = attr1 continue
else:
if isinstance(change, dict):
changed.update(change)
else:
changed[k] = change
if changed: if changed:
self.changes = Parameters(params=changed) self.changes = UsableChanges(params=changed)
return True return True
return False return False
@ -196,7 +248,7 @@ class ModuleManager(object):
try: try:
if state == "present": if state == "present":
changed = self.update() changed = self.present()
elif state == "absent": elif state == "absent":
changed = self.absent() changed = self.absent()
except iControlUnexpectedHTTPError as e: except iControlUnexpectedHTTPError as e:
@ -207,14 +259,36 @@ class ModuleManager(object):
result.update(dict(changed=changed)) result.update(dict(changed=changed))
return result return result
def exists(self): def present(self):
provision = self.client.api.tm.sys.provision if self.exists():
resource = getattr(provision, self.want.module)
resource = resource.load()
result = resource.attrs
if str(result['level']) == 'none':
return False return False
return True return self.update()
def exists(self):
if self.want.module == 'cgnat':
resource = self.client.api.tm.sys.feature_module.cgnat.load()
if resource.disabled is True:
return False
elif resource.enabled is True:
return True
try:
for x in range(0, 5):
provision = self.client.api.tm.sys.provision
resource = getattr(provision, self.want.module)
resource = resource.load()
result = resource.attrs
if str(result['level']) != 'none' and self.want.level == 'none':
return True
if str(result['level']) == 'none' and self.want.level == 'none':
return False
if str(result['level']) == self.want.level:
return True
return False
except Exception as ex:
if 'not registered' in str(ex):
return False
time.sleep(1)
def update(self): def update(self):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
@ -223,7 +297,10 @@ class ModuleManager(object):
if self.module.check_mode: if self.module.check_mode:
return True return True
self.update_on_device() result = self.update_on_device()
if self.want.module == 'cgnat':
return result
self._wait_for_module_provisioning() self._wait_for_module_provisioning()
if self.want.module == 'vcmp': if self.want.module == 'vcmp':
@ -232,8 +309,55 @@ class ModuleManager(object):
if self.want.module == 'asm': if self.want.module == 'asm':
self._wait_for_asm_ready() self._wait_for_asm_ready()
if self.want.module == 'afm':
self._wait_for_afm_ready()
return True return True
def should_reboot(self):
for x in range(0, 24):
try:
resource = self.client.api.tm.sys.dbs.db.load(name='provision.action')
if resource.value == 'reboot':
return True
elif resource.value == 'none':
time.sleep(5)
except Exception:
time.sleep(5)
return False
def reboot_device(self):
nops = 0
last_reboot = self._get_last_reboot()
try:
output = self.client.api.tm.util.bash.exec_cmd(
'run',
utilCmdArgs='-c "/sbin/reboot"'
)
if hasattr(output, 'commandResult'):
return str(output.commandResult)
except Exception:
pass
# Sleep a little to let rebooting take effect
time.sleep(20)
while nops < 6:
try:
self.client.reconnect()
next_reboot = self._get_last_reboot()
if next_reboot is None:
nops = 0
if next_reboot == last_reboot:
nops = 0
else:
nops += 1
except Exception as ex:
# This can be caused by restjavad restarting.
pass
time.sleep(10)
return None
def should_update(self): def should_update(self):
result = self._update_changed_options() result = self._update_changed_options()
if result: if result:
@ -241,11 +365,22 @@ class ModuleManager(object):
return False return False
def update_on_device(self): def update_on_device(self):
if self.want.level == 'dedicated': if self.want.module == 'cgnat':
if self.changes.cgnat:
return self.provision_cgnat_on_device()
return False
elif self.want.level == 'dedicated':
self.provision_dedicated_on_device() self.provision_dedicated_on_device()
else: else:
self.provision_non_dedicated_on_device() self.provision_non_dedicated_on_device()
def provision_cgnat_on_device(self):
resource = self.client.api.tm.sys.feature_module.cgnat.load()
resource.modify(
enabled=True
)
return True
def provision_dedicated_on_device(self): def provision_dedicated_on_device(self):
params = self.want.api_params() params = self.want.api_params()
tx = self.client.api.tm.transactions.transaction tx = self.client.api.tm.transactions.transaction
@ -269,11 +404,15 @@ class ModuleManager(object):
resource.update(**params) resource.update(**params)
def read_current_from_device(self): def read_current_from_device(self):
provision = self.client.api.tm.sys.provision if self.want.module == 'cgnat':
resource = getattr(provision, str(self.want.module)) resource = self.client.api.tm.sys.feature_module.cgnat.load()
resource = resource.load() result = resource.attrs
result = resource.attrs else:
return Parameters(params=result) provision = self.client.api.tm.sys.provision
resource = getattr(provision, str(self.want.module))
resource = resource.load()
result = resource.attrs
return ApiParameters(params=result)
def absent(self): def absent(self):
if self.exists(): if self.exists():
@ -283,7 +422,9 @@ class ModuleManager(object):
def remove(self): def remove(self):
if self.module.check_mode: if self.module.check_mode:
return True return True
self.remove_from_device() result = self.remove_from_device()
if self.want.module == 'cgnat':
return result
self._wait_for_module_provisioning() self._wait_for_module_provisioning()
# For vCMP, because it has to reboot, we also wait for mcpd to become available # For vCMP, because it has to reboot, we also wait for mcpd to become available
@ -293,16 +434,40 @@ class ModuleManager(object):
self._wait_for_reboot() self._wait_for_reboot()
self._wait_for_module_provisioning() self._wait_for_module_provisioning()
if self.should_reboot():
self.save_on_device()
self.reboot_device()
self._wait_for_module_provisioning()
if self.exists(): if self.exists():
raise F5ModuleError("Failed to de-provision the module") raise F5ModuleError("Failed to de-provision the module")
return True return True
def save_on_device(self):
command = 'tmsh save sys config'
self.client.api.tm.util.bash.exec_cmd(
'run',
utilCmdArgs='-c "{0}"'.format(command)
)
def remove_from_device(self): def remove_from_device(self):
if self.want.module == 'cgnat':
if self.changes.cgnat:
return self.deprovision_cgnat_on_device()
return False
provision = self.client.api.tm.sys.provision provision = self.client.api.tm.sys.provision
resource = getattr(provision, self.want.module) resource = getattr(provision, self.want.module)
resource = resource.load() resource = resource.load()
resource.update(level='none') resource.update(level='none')
def deprovision_cgnat_on_device(self):
resource = self.client.api.tm.sys.feature_module.cgnat.load()
resource.modify(
disabled=True
)
return True
def _wait_for_module_provisioning(self): def _wait_for_module_provisioning(self):
# To prevent things from running forever, the hack is to check # To prevent things from running forever, the hack is to check
# for mprov's status twice. If mprov is finished, then in most # for mprov's status twice. If mprov is finished, then in most
@ -370,6 +535,26 @@ class ModuleManager(object):
restarted_asm = True restarted_asm = True
time.sleep(5) time.sleep(5)
def _wait_for_afm_ready(self):
"""Waits specifically for AFM
AFM can take longer to actually start up than all the previous checks take.
This check here is specifically waiting for the Security API to stop raising
errors.
:return:
"""
nops = 0
while nops < 3:
try:
security = self.client.api.tm.security.get_collection()
if len(security) >= 0:
nops += 1
else:
nops = 0
except Exception as ex:
pass
time.sleep(5)
def _restart_asm(self): def _restart_asm(self):
try: try:
self.client.api.tm.util.bash.exec_cmd( self.client.api.tm.util.bash.exec_cmd(
@ -427,7 +612,7 @@ class ArgumentSpec(object):
choices=[ choices=[
'afm', 'am', 'sam', 'asm', 'avr', 'fps', 'afm', 'am', 'sam', 'asm', 'avr', 'fps',
'gtm', 'lc', 'ltm', 'pem', 'swg', 'ilx', 'gtm', 'lc', 'ltm', 'pem', 'swg', 'ilx',
'apm', 'vcmp' 'apm', 'vcmp', 'cgnat'
], ],
aliases=['name'] aliases=['name']
), ),

View file

@ -22,7 +22,7 @@ description:
when dealing with F5 support. It may be required that you upload this when dealing with F5 support. It may be required that you upload this
qkview to the supported channels during resolution of an SRs that you qkview to the supported channels during resolution of an SRs that you
may have opened. may have opened.
version_added: "2.4" version_added: 2.4
options: options:
filename: filename:
description: description:
@ -46,7 +46,7 @@ options:
complete_information: complete_information:
description: description:
- Include complete information in the qkview. - Include complete information in the qkview.
default: yes default: no
type: bool type: bool
exclude_core: exclude_core:
description: description:
@ -105,30 +105,23 @@ from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six import string_types from ansible.module_utils.six import string_types
from distutils.version import LooseVersion from distutils.version import LooseVersion
HAS_DEVEL_IMPORTS = False
try: try:
# Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK 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.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError 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 AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens 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 from library.module_utils.network.f5.common import f5_argument_spec
try: try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError: except ImportError:
HAS_F5SDK = False 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 HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client 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 F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters 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 cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec from ansible.module_utils.network.f5.common import f5_argument_spec
try: try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
@ -434,7 +427,10 @@ class ArgumentSpec(object):
type='bool' type='bool'
), ),
exclude=dict( exclude=dict(
type='list' type='list',
choices=[
'all', 'audit', 'secure', 'bash_history'
]
), ),
dest=dict( dest=dict(
type='path', type='path',

View file

@ -13,6 +13,7 @@ ANSIBLE_METADATA = {'metadata_version': '1.1',
'supported_by': 'community'} 'supported_by': 'community'}
DOCUMENTATION = r''' DOCUMENTATION = r'''
---
module: bigip_remote_syslog module: bigip_remote_syslog
short_description: Manipulate remote syslog settings on a BIG-IP short_description: Manipulate remote syslog settings on a BIG-IP
description: description:
@ -93,30 +94,23 @@ import re
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six import iteritems from ansible.module_utils.six import iteritems
HAS_DEVEL_IMPORTS = False
try: try:
# Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK 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.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError 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 AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens 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 from library.module_utils.network.f5.common import f5_argument_spec
try: try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError: except ImportError:
HAS_F5SDK = False 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 HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client 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 F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters 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 cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name
from ansible.module_utils.network.f5.common import f5_argument_spec from ansible.module_utils.network.f5.common import f5_argument_spec
try: try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError

View file

@ -18,13 +18,11 @@ module: bigip_routedomain
short_description: Manage route domains on a BIG-IP short_description: Manage route domains on a BIG-IP
description: description:
- Manage route domains on a BIG-IP. - Manage route domains on a BIG-IP.
version_added: "2.2" version_added: 2.2
options: options:
name: name:
description: description:
- The name of the route domain. - The name of the route domain.
- When creating a new route domain, if this value is not specified, then the
value of C(id) will be used for it.
version_added: 2.5 version_added: 2.5
bwc_policy: bwc_policy:
description: description:
@ -51,8 +49,8 @@ options:
become a required parameter. become a required parameter.
parent: parent:
description: description:
Specifies the route domain the system searches when it cannot - Specifies the route domain the system searches when it cannot
find a route in the configured domain. find a route in the configured domain.
partition: partition:
description: description:
- Partition to create the route domain on. Partitions cannot be updated - Partition to create the route domain on. Partitions cannot be updated
@ -83,14 +81,11 @@ options:
- absent - absent
strict: strict:
description: description:
- Specifies whether the system enforces cross-routing restrictions - Specifies whether the system enforces cross-routing restrictions or not.
or not. type: bool
choices:
- enabled
- disabled
vlans: vlans:
description: description:
- VLANs for the system to use in the route domain - VLANs for the system to use in the route domain.
extends_documentation_fragment: f5 extends_documentation_fragment: f5
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
@ -122,82 +117,79 @@ EXAMPLES = r'''
RETURN = r''' RETURN = r'''
id: id:
description: The ID of the route domain that was changed description: The ID of the route domain that was changed.
returned: changed returned: changed
type: int type: int
sample: 2 sample: 2
description: description:
description: The description of the route domain description: The description of the route domain.
returned: changed returned: changed
type: string type: string
sample: route domain foo sample: route domain foo
strict: strict:
description: The new strict isolation setting description: The new strict isolation setting.
returned: changed returned: changed
type: string type: string
sample: enabled sample: enabled
parent: parent:
description: The new parent route domain description: The new parent route domain.
returned: changed returned: changed
type: int type: int
sample: 0 sample: 0
vlans: vlans:
description: List of new VLANs the route domain is applied to description: List of new VLANs the route domain is applied to.
returned: changed returned: changed
type: list type: list
sample: ['/Common/http-tunnel', '/Common/socks-tunnel'] sample: ['/Common/http-tunnel', '/Common/socks-tunnel']
routing_protocol: routing_protocol:
description: List of routing protocols applied to the route domain description: List of routing protocols applied to the route domain.
returned: changed returned: changed
type: list type: list
sample: ['bfd', 'bgp'] sample: ['bfd', 'bgp']
bwc_policy: bwc_policy:
description: The new bandwidth controller description: The new bandwidth controller.
returned: changed returned: changed
type: string type: string
sample: /Common/foo sample: /Common/foo
connection_limit: connection_limit:
description: The new connection limit for the route domain description: The new connection limit for the route domain.
returned: changed returned: changed
type: int type: int
sample: 100 sample: 100
flow_eviction_policy: flow_eviction_policy:
description: The new eviction policy to use with this route domain description: The new eviction policy to use with this route domain.
returned: changed returned: changed
type: string type: string
sample: /Common/default-eviction-policy sample: /Common/default-eviction-policy
service_policy: service_policy:
description: The new service policy to use with this route domain description: The new service policy to use with this route domain.
returned: changed returned: changed
type: string type: string
sample: /Common-my-service-policy sample: /Common-my-service-policy
''' '''
try:
from f5.bigip import ManagementRoot
except ImportError:
pass # Handled via f5_utils.HAS_F5SDK
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback from ansible.module_utils.basic import env_fallback
from ansible.module_utils.ec2 import camel_dict_to_snake_dict
HAS_DEVEL_IMPORTS = False
try: try:
# Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK 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 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 fq_name
from library.module_utils.network.f5.common import f5_argument_spec from library.module_utils.network.f5.common import f5_argument_spec
try: try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError: except ImportError:
HAS_F5SDK = False 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 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 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 fq_name
from ansible.module_utils.network.f5.common import f5_argument_spec from ansible.module_utils.network.f5.common import f5_argument_spec
try: try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
@ -205,375 +197,429 @@ except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
PROTOCOLS = [ class Parameters(AnsibleF5Parameters):
'BFD', 'BGP', 'IS-IS', 'OSPFv2', 'OSPFv3', 'PIM', 'RIP', 'RIPng' api_map = {
] 'connectionLimit': 'connection_limit',
'servicePolicy': 'service_policy',
'bwcPolicy': 'bwc_policy',
'flowEvictionPolicy': 'flow_eviction_policy',
'routingProtocol': 'routing_protocol'
}
STRICTS = ['enabled', 'disabled'] api_attributes = [
'connectionLimit',
'description',
'strict',
'parent',
'servicePolicy',
'bwcPolicy',
'flowEvictionPolicy',
'routingProtocol',
'vlans',
'id'
]
returnables = [
'description',
'strict',
'parent',
'service_policy',
'bwc_policy',
'flow_eviction_policy',
'routing_protocol',
'vlans',
'connection_limit',
'id'
]
updatables = [
'description',
'strict',
'parent',
'service_policy',
'bwc_policy',
'flow_eviction_policy',
'routing_protocol',
'vlans',
'connection_limit',
'id'
]
@property
def connection_limit(self):
if self._values['connection_limit'] is None:
return None
return int(self._values['connection_limit'])
@property
def id(self):
if self._values['id'] is None:
return None
return int(self._values['id'])
class BigIpRouteDomain(object): class ApiParameters(Parameters):
@property
def strict(self):
if self._values['strict'] is None:
return None
if self._values['strict'] == 'enabled':
return True
return False
@property
def domains(self):
domains = self.read_domains_from_device()
result = [x.fullPath for x in domains]
return result
def read_domains_from_device(self):
collection = self.client.api.tm.net.route_domains.get_collection()
return collection
class ModuleParameters(Parameters):
@property
def bwc_policy(self):
if self._values['bwc_policy'] is None:
return None
return fq_name(self.partition, self._values['bwc_policy'])
@property
def flow_eviction_policy(self):
if self._values['flow_eviction_policy'] is None:
return None
return fq_name(self.partition, self._values['flow_eviction_policy'])
@property
def service_policy(self):
if self._values['service_policy'] is None:
return None
return fq_name(self.partition, self._values['service_policy'])
@property
def parent(self):
if self._values['parent'] is None:
return None
result = fq_name(self.partition, self._values['parent'])
return result
@property
def vlans(self):
if self._values['vlans'] is None:
return None
if len(self._values['vlans']) == 1 and self._values['vlans'][0] == '':
return ''
return [fq_name(self.partition, x) for x in self._values['vlans']]
@property
def name(self):
if self._values['name'] is None:
return str(self.id)
return self._values['name']
@property
def routing_protocol(self):
if self._values['routing_protocol'] is None:
return None
if len(self._values['routing_protocol']) == 1 and self._values['routing_protocol'][0] == '':
return ''
return self._values['routing_protocol']
class Changes(Parameters):
def to_return(self):
result = {}
try:
for returnable in self.returnables:
result[returnable] = getattr(self, returnable)
result = self._filter_params(result)
except Exception:
pass
return result
class UsableChanges(Changes):
@property
def strict(self):
if self._values['strict'] is None:
return None
if self._values['strict']:
return 'enabled'
return 'disabled'
class ReportableChanges(Changes):
@property
def strict(self):
if self._values['strict'] is None:
return None
if self._values['strict'] == 'enabled':
return 'yes'
return 'no'
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 routing_protocol(self):
if self.want.routing_protocol is None:
return None
if self.want.routing_protocol == '' and self.have.routing_protocol is None:
return None
if self.want.routing_protocol == '' and len(self.have.routing_protocol) > 0:
return []
if self.have.routing_protocol is None:
return self.want.routing_protocol
want = set(self.want.routing_protocol)
have = set(self.have.routing_protocol)
if want != have:
return list(want)
@property
def vlans(self):
if self.want.vlans is None:
return None
if self.want.vlans == '' and self.have.vlans is None:
return None
if self.want.vlans == '' and len(self.have.vlans) > 0:
return []
if self.have.vlans is None:
return self.want.vlans
want = set(self.want.vlans)
have = set(self.have.vlans)
if want != have:
return list(want)
class ModuleManager(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if not HAS_F5SDK: self.module = kwargs.get('module', None)
raise F5ModuleError("The python f5-sdk module is required") self.client = kwargs.get('client', None)
self.want = ModuleParameters(params=self.module.params, client=self.client)
self.have = ApiParameters(client=self.client)
self.changes = UsableChanges()
# The params that change in the module def _set_changed_options(self):
self.cparams = dict() changed = {}
for key in Parameters.returnables:
if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key)
if changed:
self.changes = UsableChanges(params=changed)
# Stores the params that are sent to the module def _update_changed_options(self):
self.params = kwargs diff = Difference(self.want, self.have)
self.api = ManagementRoot(kwargs['server'], updatables = Parameters.updatables
kwargs['user'], changed = dict()
kwargs['password'], for k in updatables:
port=kwargs['server_port'], change = diff.compare(k)
token=True) if change is None:
continue
def absent(self): else:
if not self.exists(): if isinstance(change, dict):
return False changed.update(change)
else:
if self.params['check_mode']: changed[k] = change
if changed:
self.changes = UsableChanges(params=changed)
return True return True
return False
if self.params['name'] is None: def should_update(self):
self.params['name'] = str(self.params['id']) result = self._update_changed_options()
if result:
rd = self.api.tm.net.route_domains.route_domain.load(
name=self.params['name'],
partition=self.params['partition']
)
rd.delete()
if self.exists():
raise F5ModuleError("Failed to delete the route domain")
else:
return True return True
return False
def exec_module(self):
changed = False
result = dict()
state = self.want.state
try:
if state == "present":
changed = self.present()
elif state == "absent":
changed = self.absent()
except iControlUnexpectedHTTPError as e:
raise F5ModuleError(str(e))
reportable = ReportableChanges(params=self.changes.to_return())
changes = reportable.to_return()
result.update(**changes)
result.update(dict(changed=changed))
self._announce_deprecations(result)
return result
def _announce_deprecations(self, result):
warnings = result.pop('__warnings', [])
for warning in warnings:
self.client.module.deprecate(
msg=warning['msg'],
version=warning['version']
)
def present(self): def present(self):
if self.exists(): if self.exists():
return self.update() return self.update()
else: else:
if self.params['check_mode']:
return True
return self.create() return self.create()
def read(self): def exists(self):
"""Read information and transform it result = self.client.api.tm.net.route_domains.route_domain.exists(
name=self.want.name,
The values that are returned by BIG-IP in the f5-sdk can have encoding partition=self.want.partition
attached to them as well as be completely missing in some cases.
Therefore, this method will transform the data from the BIG-IP into a
format that is more easily consumable by the rest of the class and the
parameters that are supported by the module.
"""
p = dict()
if self.params['name'] is None:
self.params['name'] = str(self.params['id'])
r = self.api.tm.net.route_domains.route_domain.load(
name=self.params['name'],
partition=self.params['partition']
) )
p['id'] = int(r.id)
p['name'] = str(r.name)
if hasattr(r, 'connectionLimit'):
p['connection_limit'] = int(r.connectionLimit)
if hasattr(r, 'description'):
p['description'] = str(r.description)
if hasattr(r, 'strict'):
p['strict'] = str(r.strict)
if hasattr(r, 'parent'):
p['parent'] = r.parent
if hasattr(r, 'vlans'):
p['vlans'] = list(set([str(x) for x in r.vlans]))
if hasattr(r, 'routingProtocol'):
p['routing_protocol'] = list(set([str(x) for x in r.routingProtocol]))
if hasattr(r, 'flowEvictionPolicy'):
p['flow_eviction_policy'] = str(r.flowEvictionPolicy)
if hasattr(r, 'bwcPolicy'):
p['bwc_policy'] = str(r.bwcPolicy)
if hasattr(r, 'servicePolicy'):
p['service_policy'] = str(r.servicePolicy)
return p
def domains(self):
result = []
domains = self.api.tm.net.route_domains.get_collection()
for domain in domains:
# Just checking for the addition of the partition here for
# different versions of BIG-IP
if '/' + self.params['partition'] + '/' in domain.name:
result.append(domain.name)
else:
full_name = '/%s/%s' % (self.params['partition'], domain.name)
result.append(full_name)
return result return result
def create(self): def update(self):
params = dict() self.have = self.read_current_from_device()
params['id'] = self.params['id'] if not self.should_update():
params['name'] = self.params['name'] return False
params['partition'] = self.params['partition'] if self.want.parent and self.want.parent not in self.have.domains:
raise F5ModuleError(
"The parent route domain was not found."
)
if self.module.check_mode:
return True
self.update_on_device()
return True
if params['name'] is None: def remove(self):
self.params['name'] = str(self.params['id']) if self.module.check_mode:
elif params['id'] is None: return True
self.remove_from_device()
if self.exists():
raise F5ModuleError("Failed to delete the resource.")
return True
def create(self):
if self.want.id is None:
raise F5ModuleError( raise F5ModuleError(
"The 'id' parameter is required when creating new route domains." "The 'id' parameter is required when creating new route domains."
) )
if self.want.parent and self.want.parent not in self.have.domains:
partition = self.params['partition']
description = self.params['description']
strict = self.params['strict']
parent = self.params['parent']
bwc_policy = self.params['bwc_policy']
vlans = self.params['vlans']
routing_protocol = self.params['routing_protocol']
connection_limit = self.params['connection_limit']
flow_eviction_policy = self.params['flow_eviction_policy']
service_policy = self.params['service_policy']
if description is not None:
params['description'] = description
if strict is not None:
params['strict'] = strict
if parent is not None:
parent = '/%s/%s' % (partition, parent)
if parent in self.domains():
params['parent'] = parent
else:
raise F5ModuleError(
"The parent route domain was not found"
)
if bwc_policy is not None:
policy = '/%s/%s' % (partition, bwc_policy)
params['bwcPolicy'] = policy
if vlans is not None:
params['vlans'] = []
for vlan in vlans:
vname = '/%s/%s' % (partition, vlan)
params['vlans'].append(vname)
if routing_protocol is not None:
params['routingProtocol'] = []
for protocol in routing_protocol:
if protocol in PROTOCOLS:
params['routingProtocol'].append(protocol)
else:
raise F5ModuleError(
"routing_protocol must be one of: %s" % (PROTOCOLS)
)
if connection_limit is not None:
params['connectionLimit'] = connection_limit
if flow_eviction_policy is not None:
policy = '/%s/%s' % (partition, flow_eviction_policy)
params['flowEvictionPolicy'] = policy
if service_policy is not None:
policy = '/%s/%s' % (partition, service_policy)
params['servicePolicy'] = policy
self.api.tm.net.route_domains.route_domain.create(**params)
exists = self.api.tm.net.route_domains.route_domain.exists(
name=self.params['name'],
partition=self.params['partition']
)
if exists:
return True
else:
raise F5ModuleError( raise F5ModuleError(
"An error occurred while creating the route domain" "The parent route domain was not found."
) )
self._set_changed_options()
def update(self): if self.module.check_mode:
changed = False return True
params = dict() self.create_on_device()
current = self.read()
if self.params['name'] is None:
self.params['name'] = str(self.params['id'])
check_mode = self.params['check_mode']
partition = self.params['partition']
description = self.params['description']
strict = self.params['strict']
parent = self.params['parent']
bwc_policy = self.params['bwc_policy']
vlans = self.params['vlans']
routing_protocol = self.params['routing_protocol']
connection_limit = self.params['connection_limit']
flow_eviction_policy = self.params['flow_eviction_policy']
service_policy = self.params['service_policy']
if description is not None:
if 'description' in current:
if description != current['description']:
params['description'] = description
else:
params['description'] = description
if strict is not None:
if strict != current['strict']:
params['strict'] = strict
if parent is not None:
parent = '/%s/%s' % (partition, parent)
if 'parent' in current:
if parent != current['parent']:
params['parent'] = parent
else:
params['parent'] = parent
if bwc_policy is not None:
policy = '/%s/%s' % (partition, bwc_policy)
if 'bwc_policy' in current:
if policy != current['bwc_policy']:
params['bwcPolicy'] = policy
else:
params['bwcPolicy'] = policy
if vlans is not None:
tmp = set()
for vlan in vlans:
vname = '/%s/%s' % (partition, vlan)
tmp.add(vname)
tmp = list(tmp)
if 'vlans' in current:
if tmp != current['vlans']:
params['vlans'] = tmp
else:
params['vlans'] = tmp
if routing_protocol is not None:
tmp = set()
for protocol in routing_protocol:
if protocol in PROTOCOLS:
tmp.add(protocol)
else:
raise F5ModuleError(
"routing_protocol must be one of: %s" % (PROTOCOLS)
)
tmp = list(tmp)
if 'routing_protocol' in current:
if tmp != current['routing_protocol']:
params['routingProtocol'] = tmp
else:
params['routingProtocol'] = tmp
if connection_limit is not None:
if connection_limit != current['connection_limit']:
params['connectionLimit'] = connection_limit
if flow_eviction_policy is not None:
policy = '/%s/%s' % (partition, flow_eviction_policy)
if 'flow_eviction_policy' in current:
if policy != current['flow_eviction_policy']:
params['flowEvictionPolicy'] = policy
else:
params['flowEvictionPolicy'] = policy
if service_policy is not None:
policy = '/%s/%s' % (partition, service_policy)
if 'service_policy' in current:
if policy != current['service_policy']:
params['servicePolicy'] = policy
else:
params['servicePolicy'] = policy
if params:
changed = True
self.cparams = camel_dict_to_snake_dict(params)
if check_mode:
return changed
else:
return changed
try:
rd = self.api.tm.net.route_domains.route_domain.load(
name=self.params['name'],
partition=self.params['partition']
)
rd.update(**params)
rd.refresh()
except iControlUnexpectedHTTPError as e:
raise F5ModuleError(e)
return True return True
def exists(self): def create_on_device(self):
if self.params['name'] is None: params = self.changes.api_params()
self.params['name'] = str(self.params['id']) self.client.api.tm.net.route_domains.route_domain.create(
return self.api.tm.net.route_domains.route_domain.exists( name=self.want.name,
name=self.params['name'], partition=self.want.partition,
partition=self.params['partition'] **params
) )
def exec_module(self): def update_on_device(self):
result = dict() params = self.changes.api_params()
state = self.params['state'] resource = self.client.api.tm.net.route_domains.route_domain.load(
name=self.want.name,
partition=self.want.partition
)
resource.modify(**params)
if state == "present": def absent(self):
changed = self.present() if self.exists():
current = self.read() return self.remove()
result.update(current) return False
elif state == "absent":
changed = self.absent()
result.update(dict(changed=changed)) def remove_from_device(self):
return result resource = self.client.api.tm.net.route_domains.route_domain.load(
name=self.want.name,
partition=self.want.partition
)
if resource:
resource.delete()
def read_current_from_device(self):
resource = self.client.api.tm.net.route_domains.route_domain.load(
name=self.want.name,
partition=self.want.partition
)
result = resource.attrs
return ApiParameters(params=result, client=self.client)
class ArgumentSpec(object):
def __init__(self):
self.supports_check_mode = True
argument_spec = dict(
name=dict(),
id=dict(type='int'),
description=dict(),
strict=dict(type='bool'),
parent=dict(type='int'),
vlans=dict(type='list'),
routing_protocol=dict(
type='list',
choices=['BFD', 'BGP', 'IS-IS', 'OSPFv2', 'OSPFv3', 'PIM', 'RIP', 'RIPng']
),
bwc_policy=dict(),
connection_limit=dict(type='int'),
flow_eviction_policy=dict(),
service_policy=dict(),
partition=dict(
default='Common',
fallback=(env_fallback, ['F5_PARTITION'])
),
state=dict(
default='present',
choices=['present', 'absent']
)
)
self.argument_spec = {}
self.argument_spec.update(f5_argument_spec)
self.argument_spec.update(argument_spec)
self.required_one_of = [
['name', 'id']
]
def main(): def main():
argument_spec = f5_argument_spec spec = ArgumentSpec()
meta_args = dict(
name=dict(),
id=dict(type='int'),
description=dict(),
strict=dict(choices=STRICTS),
parent=dict(type='int'),
vlans=dict(type='list'),
routing_protocol=dict(type='list'),
bwc_policy=dict(),
connection_limit=dict(type='int',),
flow_eviction_policy=dict(),
service_policy=dict(),
partition=dict(
default='Common',
fallback=(env_fallback, ['F5_PARTITION'])
),
state=dict(
default='present',
choices=['present', 'absent']
)
)
argument_spec.update(meta_args)
module = AnsibleModule( module = AnsibleModule(
argument_spec=argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=True, supports_check_mode=spec.supports_check_mode
required_one_of=[['name', 'id']]
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required")
try: try:
obj = BigIpRouteDomain(check_mode=module.check_mode, **module.params) client = F5Client(**module.params)
result = obj.exec_module() mm = ModuleManager(module=module, client=client)
results = mm.exec_module()
module.exit_json(**result) cleanup_tokens(client)
except F5ModuleError as e: module.exit_json(**results)
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__':

View file

@ -19,7 +19,7 @@ short_description: Manage address lists on BIG-IP AFM
description: description:
- Manages the AFM address lists on a BIG-IP. This module can be used to add - Manages the AFM address lists on a BIG-IP. This module can be used to add
and remove address list entries. and remove address list entries.
version_added: "2.5" version_added: 2.5
options: options:
name: name:
description: description:
@ -54,6 +54,7 @@ options:
- Individual addresses that you want to add to the list. These addresses differ - Individual addresses that you want to add to the list. These addresses differ
from ranges, and lists of lists such as what can be used in C(address_ranges) from ranges, and lists of lists such as what can be used in C(address_ranges)
and C(address_lists) respectively. and C(address_lists) respectively.
- This list can also include networks that have CIDR notation.
address_ranges: address_ranges:
description: description:
- A list of address ranges where the range starts with a port number, is followed - A list of address ranges where the range starts with a port number, is followed
@ -152,30 +153,25 @@ import re
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback from ansible.module_utils.basic import env_fallback
HAS_DEVEL_IMPORTS = False
try: try:
# Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK 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.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError 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 AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens 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 fq_name
from library.module_utils.network.f5.common import f5_argument_spec from library.module_utils.network.f5.common import f5_argument_spec
try: try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError: except ImportError:
HAS_F5SDK = False 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 HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client 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 F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters 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 cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name from ansible.module_utils.network.f5.common import fq_name
from ansible.module_utils.network.f5.common import f5_argument_spec from ansible.module_utils.network.f5.common import f5_argument_spec
try: try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
@ -219,11 +215,6 @@ class Parameters(AnsibleF5Parameters):
pass pass
return 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
class ApiParameters(Parameters): class ApiParameters(Parameters):
@property @property
@ -538,8 +529,15 @@ class ModuleParameters(Parameters):
netaddr.IPAddress(x) netaddr.IPAddress(x)
except netaddr.core.AddrFormatError: except netaddr.core.AddrFormatError:
raise F5ModuleError( raise F5ModuleError(
"Address {0} must be either an IPv4 or IPv6 address".format(x) "Address {0} must be either an IPv4 or IPv6 address or network.".format(x)
) )
except ValueError:
try:
netaddr.IPNetwork(x)
except netaddr.core.AddrFormatError:
raise F5ModuleError(
"Address {0} must be either an IPv4 or IPv6 address or network.".format(x)
)
result = [str(x) for x in self._values['addresses']] result = [str(x) for x in self._values['addresses']]
result = sorted(result) result = sorted(result)
return result return result
@ -573,7 +571,7 @@ class ModuleParameters(Parameters):
return None return None
result = [] result = []
for x in self._values['address_lists']: for x in self._values['address_lists']:
item = self._fqdn_name(x) item = fq_name(self.partition, x)
result.append(item) result.append(item)
result = sorted(result) result = sorted(result)
return result return result

View file

@ -19,7 +19,7 @@ short_description: Manage port lists on BIG-IP AFM
description: description:
- Manages the AFM port lists on a BIG-IP. This module can be used to add - Manages the AFM port lists on a BIG-IP. This module can be used to add
and remove port list entries. and remove port list entries.
version_added: "2.5" version_added: 2.5
options: options:
name: name:
description: description:
@ -29,7 +29,6 @@ options:
description: description:
- Device partition to manage resources on. - Device partition to manage resources on.
default: Common default: Common
version_added: 2.5
description: description:
description: description:
- Description of the port list - Description of the port list
@ -56,7 +55,6 @@ options:
choices: choices:
- present - present
- absent - absent
version_added: 2.5
extends_documentation_fragment: f5 extends_documentation_fragment: f5
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
@ -163,30 +161,25 @@ port_lists:
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback from ansible.module_utils.basic import env_fallback
HAS_DEVEL_IMPORTS = False
try: try:
# Sideband repository used for dev
from library.module_utils.network.f5.bigip import HAS_F5SDK 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.bigip import F5Client
from library.module_utils.network.f5.common import F5ModuleError 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 AnsibleF5Parameters
from library.module_utils.network.f5.common import cleanup_tokens 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 fq_name
from library.module_utils.network.f5.common import f5_argument_spec from library.module_utils.network.f5.common import f5_argument_spec
try: try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError: except ImportError:
HAS_F5SDK = False 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 HAS_F5SDK
from ansible.module_utils.network.f5.bigip import F5Client 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 F5ModuleError
from ansible.module_utils.network.f5.common import AnsibleF5Parameters 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 cleanup_tokens
from ansible.module_utils.network.f5.common import fqdn_name from ansible.module_utils.network.f5.common import fq_name
from ansible.module_utils.network.f5.common import f5_argument_spec from ansible.module_utils.network.f5.common import f5_argument_spec
try: try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
@ -211,11 +204,6 @@ class Parameters(AnsibleF5Parameters):
'description', 'ports', 'port_ranges', 'port_lists' 'description', 'ports', 'port_ranges', 'port_lists'
] ]
def _fqdn_name(self, value):
if value is not None and not value.startswith('/'):
return '/{0}/{1}'.format(self.partition, value)
return value
class ApiParameters(Parameters): class ApiParameters(Parameters):
@property @property
@ -296,7 +284,7 @@ class ModuleParameters(Parameters):
return None return None
result = [] result = []
for x in self._values['port_lists']: for x in self._values['port_lists']:
item = self._fqdn_name(x) item = fq_name(self.partition, x)
result.append(item) result.append(item)
return result return result

View file

@ -1081,10 +1081,6 @@ lib/ansible/modules/network/f5/bigip_monitor_snmp_dca.py E326
lib/ansible/modules/network/f5/bigip_policy.py E324 lib/ansible/modules/network/f5/bigip_policy.py E324
lib/ansible/modules/network/f5/bigip_pool.py E326 lib/ansible/modules/network/f5/bigip_pool.py E326
lib/ansible/modules/network/f5/bigip_profile_client_ssl.py E324 lib/ansible/modules/network/f5/bigip_profile_client_ssl.py E324
lib/ansible/modules/network/f5/bigip_provision.py E326
lib/ansible/modules/network/f5/bigip_qkview.py E324
lib/ansible/modules/network/f5/bigip_qkview.py E326
lib/ansible/modules/network/f5/bigip_routedomain.py E326
lib/ansible/modules/network/f5/bigip_selfip.py E324 lib/ansible/modules/network/f5/bigip_selfip.py E324
lib/ansible/modules/network/f5/bigip_sys_global.py E326 lib/ansible/modules/network/f5/bigip_sys_global.py E326
lib/ansible/modules/network/f5/bigip_virtual_server.py E326 lib/ansible/modules/network/f5/bigip_virtual_server.py E326

View file

@ -0,0 +1,36 @@
{
"kind": "tm:net:route-domain:route-domainstate",
"name": "0",
"partition": "Common",
"fullPath": "/Common/0",
"generation": 1,
"selfLink": "https://localhost/mgmt/tm/net/route-domain/~Common~0?ver=13.1.0",
"connectionLimit": 0,
"id": 0,
"strict": "enabled",
"throughputCapacity": 0,
"vlans": [
"/Common/net1",
"/Common/internal",
"/Common/net2",
"/Common/socks-tunnel",
"/Common/http-tunnel"
],
"vlansReference": [
{
"link": "https://localhost/mgmt/tm/net/vlan/~Common~net1?ver=13.1.0"
},
{
"link": "https://localhost/mgmt/tm/net/vlan/~Common~internal?ver=13.1.0"
},
{
"link": "https://localhost/mgmt/tm/net/vlan/~Common~net2?ver=13.1.0"
},
{
"link": "https://localhost/mgmt/tm/net/tunnels/tunnel/~Common~socks-tunnel?ver=13.1.0"
},
{
"link": "https://localhost/mgmt/tm/net/tunnels/tunnel/~Common~http-tunnel?ver=13.1.0"
}
]
}

View file

@ -20,9 +20,9 @@ from ansible.compat.tests.mock import patch
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_provision import Parameters from library.modules.bigip_provision import Parameters
from library.bigip_provision import ModuleManager from library.modules.bigip_provision import ModuleManager
from library.bigip_provision import ArgumentSpec from library.modules.bigip_provision import ArgumentSpec
from library.module_utils.network.f5.common import F5ModuleError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common 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
@ -107,6 +107,8 @@ 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.update_on_device = Mock(return_value=True) mm.update_on_device = Mock(return_value=True)
mm.read_current_from_device = Mock(return_value=current) mm.read_current_from_device = Mock(return_value=current)
mm.reboot_device = Mock(return_value=True)
mm.save_on_device = Mock(return_value=True)
# this forced sleeping can cause these tests to take 15 # this forced sleeping can cause these tests to take 15
# or more seconds to run. This is deliberate. # or more seconds to run. This is deliberate.

View file

@ -20,11 +20,11 @@ from ansible.compat.tests.mock import patch
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_qkview import Parameters from library.modules.bigip_qkview import Parameters
from library.bigip_qkview import ModuleManager from library.modules.bigip_qkview import ModuleManager
from library.bigip_qkview import MadmLocationManager from library.modules.bigip_qkview import MadmLocationManager
from library.bigip_qkview import BulkLocationManager from library.modules.bigip_qkview import BulkLocationManager
from library.bigip_qkview import ArgumentSpec from library.modules.bigip_qkview import ArgumentSpec
from library.module_utils.network.f5.common import F5ModuleError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common 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

View file

@ -20,11 +20,11 @@ from ansible.compat.tests.mock import patch
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_remote_syslog import Parameters from library.modules.bigip_remote_syslog import Parameters
from library.bigip_remote_syslog import ModuleManager from library.modules.bigip_remote_syslog import ModuleManager
from library.bigip_remote_syslog import ArgumentSpec from library.modules.bigip_remote_syslog import ArgumentSpec
from library.bigip_remote_syslog import HAS_F5SDK from library.modules.bigip_remote_syslog import HAS_F5SDK
from library.bigip_remote_syslog import HAS_NETADDR from library.modules.bigip_remote_syslog import HAS_NETADDR
from library.module_utils.network.f5.common import F5ModuleError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common 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

View file

@ -0,0 +1,125 @@
# -*- coding: utf-8 -*-
#
# Copyright: (c) 2017, F5 Networks Inc.
# GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os
import json
import pytest
import sys
from nose.plugins.skip import SkipTest
if sys.version_info < (2, 7):
raise SkipTest("F5 Ansible modules require Python >= 2.7")
from ansible.compat.tests import unittest
from ansible.compat.tests.mock import Mock
from ansible.compat.tests.mock import patch
from ansible.module_utils.basic import AnsibleModule
try:
from library.modules.bigip_routedomain import ApiParameters
from library.modules.bigip_routedomain import ModuleParameters
from library.modules.bigip_routedomain import ModuleManager
from library.modules.bigip_routedomain 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
except ImportError:
try:
from ansible.modules.network.f5.bigip_routedomain import ApiParameters
from ansible.modules.network.f5.bigip_routedomain import ModuleParameters
from ansible.modules.network.f5.bigip_routedomain import ModuleManager
from ansible.modules.network.f5.bigip_routedomain 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
except ImportError:
raise SkipTest("F5 Ansible modules require the f5-sdk Python library")
fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures')
fixture_data = {}
def load_fixture(name):
path = os.path.join(fixture_path, name)
if path in fixture_data:
return fixture_data[path]
with open(path) as f:
data = f.read()
try:
data = json.loads(data)
except Exception:
pass
fixture_data[path] = data
return data
class TestParameters(unittest.TestCase):
def test_module_parameters(self):
args = dict(
name='foo',
id='1234',
description='my description',
strict=True,
parent='parent1',
vlans=['vlan1', 'vlan2'],
routing_protocol=['BFD', 'BGP'],
bwc_policy='bwc1',
connection_limit=200,
flow_eviction_policy='evict1',
service_policy='service1'
)
p = ModuleParameters(params=args)
assert p.name == 'foo'
assert p.id == 1234
assert p.description == 'my description'
assert p.strict is True
assert p.connection_limit == 200
def test_api_parameters(self):
args = load_fixture('load_net_route_domain_1.json')
p = ApiParameters(params=args)
assert len(p.vlans) == 5
assert p.id == 0
assert p.strict is True
assert p.connection_limit == 0
class TestManager(unittest.TestCase):
def setUp(self):
self.spec = ArgumentSpec()
def test_create(self, *args):
set_module_args(dict(
name='foo',
id=1234,
password='password',
server='localhost',
user='admin'
))
module = AnsibleModule(
argument_spec=self.spec.argument_spec,
supports_check_mode=self.spec.supports_check_mode
)
mm = ModuleManager(module=module)
# Override methods to force specific logic in the module to happen
mm.exists = Mock(return_value=False)
mm.create_on_device = Mock(return_value=True)
results = mm.exec_module()
assert results['changed'] is True
assert results['id'] == 1234

View file

@ -21,16 +21,15 @@ from ansible.compat.tests.mock import patch
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_security_address_list import ApiParameters from library.modules.bigip_security_address_list import ApiParameters
from library.bigip_security_address_list import ModuleParameters from library.modules.bigip_security_address_list import ModuleParameters
from library.bigip_security_address_list import ModuleManager from library.modules.bigip_security_address_list import ModuleManager
from library.bigip_security_address_list import ArgumentSpec from library.modules.bigip_security_address_list import ArgumentSpec
from library.module_utils.network.f5.common import F5ModuleError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common 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_security_address_list import Parameters
from ansible.modules.network.f5.bigip_security_address_list import ApiParameters from ansible.modules.network.f5.bigip_security_address_list import ApiParameters
from ansible.modules.network.f5.bigip_security_address_list import ModuleParameters from ansible.modules.network.f5.bigip_security_address_list import ModuleParameters
from ansible.modules.network.f5.bigip_security_address_list import ModuleManager from ansible.modules.network.f5.bigip_security_address_list import ModuleManager

View file

@ -21,16 +21,15 @@ from ansible.compat.tests.mock import patch
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
try: try:
from library.bigip_security_port_list import ApiParameters from library.modules.bigip_security_port_list import ApiParameters
from library.bigip_security_port_list import ModuleParameters from library.modules.bigip_security_port_list import ModuleParameters
from library.bigip_security_port_list import ModuleManager from library.modules.bigip_security_port_list import ModuleManager
from library.bigip_security_port_list import ArgumentSpec from library.modules.bigip_security_port_list import ArgumentSpec
from library.module_utils.network.f5.common import F5ModuleError from library.module_utils.network.f5.common import F5ModuleError
from library.module_utils.network.f5.common 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_security_port_list import Parameters
from ansible.modules.network.f5.bigip_security_port_list import ApiParameters from ansible.modules.network.f5.bigip_security_port_list import ApiParameters
from ansible.modules.network.f5.bigip_security_port_list import ModuleParameters from ansible.modules.network.f5.bigip_security_port_list import ModuleParameters
from ansible.modules.network.f5.bigip_security_port_list import ModuleManager from ansible.modules.network.f5.bigip_security_port_list import ModuleManager