Fixes for bigip_gtm_wide_ip (#39931)

Added the irules parameter. Misc corrections of invalid parameter
names and internal behaviors.
pull/4420/head
Tim Rupp 2018-05-09 17:28:07 -07:00 committed by GitHub
parent 1cf07028d4
commit ad5fdf5eb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 151 additions and 70 deletions

View File

@ -17,13 +17,15 @@ module: bigip_gtm_wide_ip
short_description: Manages F5 BIG-IP GTM wide ip short_description: Manages F5 BIG-IP GTM wide ip
description: description:
- Manages F5 BIG-IP GTM wide ip. - Manages F5 BIG-IP GTM wide ip.
version_added: "2.0" version_added: 2.0
options: options:
pool_lb_method: pool_lb_method:
description: description:
- Specifies the load balancing method used to select a pool in this wide - Specifies the load balancing method used to select a pool in this wide
IP. This setting is relevant only when multiple pools are configured IP. This setting is relevant only when multiple pools are configured
for a wide IP. for a wide IP.
- The C(round_robin) value is deprecated and will be removed in Ansible 2.9.
- The C(global_availability) value is deprecated and will be removed in Ansible 2.9.
required: True required: True
aliases: ['lb_method'] aliases: ['lb_method']
choices: choices:
@ -31,6 +33,8 @@ options:
- ratio - ratio
- topology - topology
- global-availability - global-availability
- global_availability
- round_robin
version_added: 2.5 version_added: 2.5
name: name:
description: description:
@ -80,13 +84,19 @@ options:
suboptions: suboptions:
name: name:
description: description:
- The name of the pool to include - The name of the pool to include.
required: true required: True
ratio: ratio:
description: description:
- Ratio for the pool. - Ratio for the pool.
- The system uses this number with the Ratio load balancing method. - The system uses this number with the Ratio load balancing method.
version_added: 2.5 version_added: 2.5
irules:
version_added: 2.6
description:
- List of rules to be applied.
- If you want to remove all existing iRules, specify a single empty value; C("").
See the documentation for an example.
extends_documentation_fragment: f5 extends_documentation_fragment: f5
author: author:
- Tim Rupp (@caphrim007) - Tim Rupp (@caphrim007)
@ -98,9 +108,54 @@ EXAMPLES = r'''
server: lb.mydomain.com server: lb.mydomain.com
user: admin user: admin
password: secret password: secret
lb_method: round-robin pool_lb_method: round-robin
name: my-wide-ip.example.com name: my-wide-ip.example.com
delegate_to: localhost delegate_to: localhost
- name: Add iRules to the Wide IP
bigip_gtm_wide_ip:
server: lb.mydomain.com
user: admin
password: secret
pool_lb_method: round-robin
name: my-wide-ip.example.com
irules:
- irule1
- irule2
delegate_to: localhost
- name: Remove one iRule from the Virtual Server
bigip_gtm_wide_ip:
server: lb.mydomain.com
user: admin
password: secret
pool_lb_method: round-robin
name: my-wide-ip.example.com
irules:
- irule1
delegate_to: localhost
- name: Remove all iRules from the Virtual Server
bigip_gtm_wide_ip:
server: lb.mydomain.com
user: admin
password: secret
pool_lb_method: round-robin
name: my-wide-ip.example.com
irules: ""
delegate_to: localhost
- name: Assign a pool with ratio to the Wide IP
bigip_gtm_wide_ip:
server: lb.mydomain.com
user: admin
password: secret
pool_lb_method: round-robin
name: my-wide-ip.example.com
pools:
- name: pool1
ratio: 100
delegate_to: localhost
''' '''
RETURN = r''' RETURN = r'''
@ -114,40 +169,40 @@ state:
returned: changed returned: changed
type: string type: string
sample: disabled sample: disabled
irules:
description: iRules set on the Wide IP.
returned: changed
type: list
sample: ['/Common/irule1', '/Common/irule2']
''' '''
import re
from ansible.module_utils.six import iteritems
from distutils.version import LooseVersion
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.six import iteritems
HAS_DEVEL_IMPORTS = False from distutils.version import LooseVersion
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 is_valid_fqdn
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 is_valid_fqdn
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
@ -157,16 +212,21 @@ except ImportError:
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
api_map = { api_map = {
'poolLbMode': 'pool_lb_method' 'poolLbMode': 'pool_lb_method',
'rules': 'irules',
} }
updatables = ['pool_lb_method', 'state', 'pools']
returnables = ['name', 'pool_lb_method', 'state', 'pools']
api_attributes = ['poolLbMode', 'enabled', 'disabled', 'pools']
def _fqdn_name(self, value): updatables = [
if value is not None and not value.startswith('/'): 'pool_lb_method', 'state', 'pools', 'irules', 'enabled', 'disabled'
return '/{0}/{1}'.format(self.partition, value) ]
return value
returnables = [
'name', 'pool_lb_method', 'state', 'pools', 'irules'
]
api_attributes = [
'poolLbMode', 'enabled', 'disabled', 'pools', 'rules'
]
class ApiParameters(Parameters): class ApiParameters(Parameters):
@ -204,25 +264,15 @@ class ApiParameters(Parameters):
class ModuleParameters(Parameters): class ModuleParameters(Parameters):
@property @property
def pool_lb_method(self): def pool_lb_method(self):
deprecated = [ if self._values['pool_lb_method'] is None:
'return_to_dns', 'null', 'static_persist', 'vs_capacity',
'least_conn', 'lowest_rtt', 'lowest_hops', 'packet_rate', 'cpu',
'hit_ratio', 'qos', 'bps', 'drop_packet', 'explicit_ip',
'connection_rate', 'vs_score'
]
if self._values['lb_method'] is None:
return None return None
lb_method = str(self._values['lb_method']) lb_method = str(self._values['pool_lb_method'])
if lb_method in deprecated: if lb_method == 'global_availability':
raise F5ModuleError(
"The provided lb_method is not supported"
)
elif lb_method == 'global_availability':
if self._values['__warnings'] is None: if self._values['__warnings'] is None:
self._values['__warnings'] = [] self._values['__warnings'] = []
self._values['__warnings'].append( self._values['__warnings'].append(
dict( dict(
msg='The provided lb_method is deprecated', msg='The provided pool_lb_method is deprecated',
version='2.4' version='2.4'
) )
) )
@ -232,7 +282,7 @@ class ModuleParameters(Parameters):
self._values['__warnings'] = [] self._values['__warnings'] = []
self._values['__warnings'].append( self._values['__warnings'].append(
dict( dict(
msg='The provided lb_method is deprecated', msg='The provided pool_lb_method is deprecated',
version='2.4' version='2.4'
) )
) )
@ -249,7 +299,7 @@ class ModuleParameters(Parameters):
def name(self): def name(self):
if self._values['name'] is None: if self._values['name'] is None:
return None return None
if not re.search(r'.*\..*\..*', self._values['name']): if not is_valid_fqdn(self._values['name']):
raise F5ModuleError( raise F5ModuleError(
"The provided name must be a valid FQDN" "The provided name must be a valid FQDN"
) )
@ -286,12 +336,28 @@ class ModuleParameters(Parameters):
return None return None
for item in self._values['pools']: for item in self._values['pools']:
pool = dict() pool = dict()
if 'name' not in item:
raise F5ModuleError(
"'name' is a required key for items in the list of pools."
)
if 'ratio' in item: if 'ratio' in item:
pool['ratio'] = item['ratio'] pool['ratio'] = item['ratio']
pool['name'] = self._fqdn_name(item['name']) pool['name'] = fq_name(self.partition, item['name'])
result.append(pool) result.append(pool)
return result return result
@property
def irules(self):
results = []
if self._values['irules'] is None:
return None
if len(self._values['irules']) == 1 and self._values['irules'][0] == '':
return ''
for irule in self._values['irules']:
result = fq_name(self.partition, irule)
results.append(result)
return results
class Changes(Parameters): class Changes(Parameters):
def to_return(self): def to_return(self):
@ -310,7 +376,13 @@ class Changes(Parameters):
class UsableChanges(Changes): class UsableChanges(Changes):
pass @property
def irules(self):
if self._values['irules'] is None:
return None
if self._values['irules'] == '':
return []
return self._values['irules']
class ReportableChanges(Changes): class ReportableChanges(Changes):
@ -375,6 +447,17 @@ class Difference(object):
result = self._diff_complex_items(self.want.pools, self.have.pools) result = self._diff_complex_items(self.want.pools, self.have.pools)
return result return result
@property
def irules(self):
if self.want.irules is None:
return None
if self.want.irules == '' and self.have.irules is None:
return None
if self.want.irules == '' and len(self.have.irules) > 0:
return []
if sorted(set(self.want.irules)) != sorted(set(self.have.irules)):
return self.want.irules
class ModuleManager(object): class ModuleManager(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -466,16 +549,16 @@ class BaseManager(object):
) )
def present(self): def present(self):
if self.want.lb_method is None:
raise F5ModuleError(
"The 'lb_method' option is required when state is 'present'"
)
if self.exists(): if self.exists():
return self.update() return self.update()
else: else:
return self.create() return self.create()
def create(self): def create(self):
if self.want.pool_lb_method is None:
raise F5ModuleError(
"The 'pool_lb_method' option is required when state is 'present'"
)
self._set_changed_options() self._set_changed_options()
if self.module.check_mode: if self.module.check_mode:
return True return True
@ -519,7 +602,7 @@ class UntypedManager(BaseManager):
) )
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.gtm.wideips.wipeip.load( result = self.client.api.tm.gtm.wideips.wipeip.load(
name=self.want.name, name=self.want.name,
partition=self.want.partition partition=self.want.partition
@ -535,7 +618,7 @@ class UntypedManager(BaseManager):
return ApiParameters(params=result) return ApiParameters(params=result)
def create_on_device(self): def create_on_device(self):
params = self.want.api_params() params = self.changes.api_params()
self.client.api.tm.gtm.wideips.wideip.create( self.client.api.tm.gtm.wideips.wideip.create(
name=self.want.name, name=self.want.name,
partition=self.want.partition, partition=self.want.partition,
@ -580,7 +663,7 @@ class TypedManager(BaseManager):
return result return result
def update_on_device(self): def update_on_device(self):
params = self.want.api_params() params = self.changes.api_params()
wideips = self.client.api.tm.gtm.wideips wideips = self.client.api.tm.gtm.wideips
collection = getattr(wideips, self.collection) collection = getattr(wideips, self.collection)
resource = getattr(collection, self.want.type) resource = getattr(collection, self.want.type)
@ -602,7 +685,7 @@ class TypedManager(BaseManager):
return ApiParameters(params=result) return ApiParameters(params=result)
def create_on_device(self): def create_on_device(self):
params = self.want.api_params() params = self.changes.api_params()
wideips = self.client.api.tm.gtm.wideips wideips = self.client.api.tm.gtm.wideips
collection = getattr(wideips, self.collection) collection = getattr(wideips, self.collection)
resource = getattr(collection, self.want.type) resource = getattr(collection, self.want.type)
@ -626,16 +709,12 @@ class TypedManager(BaseManager):
class ArgumentSpec(object): class ArgumentSpec(object):
def __init__(self): def __init__(self):
deprecated = [ lb_method_choices = [
'return_to_dns', 'null', 'round_robin', 'static_persist', 'round-robin', 'topology', 'ratio', 'global-availability',
'global_availability', 'vs_capacity', 'least_conn', 'lowest_rtt',
'lowest_hops', 'packet_rate', 'cpu', 'hit_ratio', 'qos', 'bps', # TODO(Remove in Ansible 2.9)
'drop_packet', 'explicit_ip', 'connection_rate', 'vs_score' 'round_robin', 'global_availability'
] ]
supported = [
'round-robin', 'topology', 'ratio', 'global-availability'
]
lb_method_choices = deprecated + supported
self.supports_check_mode = True self.supports_check_mode = True
argument_spec = dict( argument_spec = dict(
pool_lb_method=dict( pool_lb_method=dict(
@ -665,7 +744,10 @@ class ArgumentSpec(object):
partition=dict( partition=dict(
default='Common', default='Common',
fallback=(env_fallback, ['F5_PARTITION']) fallback=(env_fallback, ['F5_PARTITION'])
) ),
irules=dict(
type='list',
),
) )
self.argument_spec = {} self.argument_spec = {}
self.argument_spec.update(f5_argument_spec) self.argument_spec.update(f5_argument_spec)

View File

@ -1072,7 +1072,6 @@ lib/ansible/modules/network/f5/bigip_gtm_facts.py E326
lib/ansible/modules/network/f5/bigip_gtm_pool.py E324 lib/ansible/modules/network/f5/bigip_gtm_pool.py E324
lib/ansible/modules/network/f5/bigip_gtm_pool.py E326 lib/ansible/modules/network/f5/bigip_gtm_pool.py E326
lib/ansible/modules/network/f5/bigip_gtm_server.py E326 lib/ansible/modules/network/f5/bigip_gtm_server.py E326
lib/ansible/modules/network/f5/bigip_gtm_wide_ip.py E326
lib/ansible/modules/network/f5/bigip_iapp_service.py E324 lib/ansible/modules/network/f5/bigip_iapp_service.py E324
lib/ansible/modules/network/f5/bigip_iapp_service.py E325 lib/ansible/modules/network/f5/bigip_iapp_service.py E325
lib/ansible/modules/network/f5/bigip_monitor_snmp_dca.py E326 lib/ansible/modules/network/f5/bigip_monitor_snmp_dca.py E326

View File

@ -21,12 +21,12 @@ 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_gtm_wide_ip import ApiParameters from library.modules.bigip_gtm_wide_ip import ApiParameters
from library.bigip_gtm_wide_ip import ModuleParameters from library.modules.bigip_gtm_wide_ip import ModuleParameters
from library.bigip_gtm_wide_ip import ModuleManager from library.modules.bigip_gtm_wide_ip import ModuleManager
from library.bigip_gtm_wide_ip import ArgumentSpec from library.modules.bigip_gtm_wide_ip import ArgumentSpec
from library.bigip_gtm_wide_ip import UntypedManager from library.modules.bigip_gtm_wide_ip import UntypedManager
from library.bigip_gtm_wide_ip import TypedManager from library.modules.bigip_gtm_wide_ip import TypedManager
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
@ -70,7 +70,7 @@ class TestParameters(unittest.TestCase):
def test_module_parameters(self): def test_module_parameters(self):
args = dict( args = dict(
name='foo.baz.bar', name='foo.baz.bar',
lb_method='round-robin', pool_lb_method='round-robin',
) )
p = ModuleParameters(params=args) p = ModuleParameters(params=args)
assert p.name == 'foo.baz.bar' assert p.name == 'foo.baz.bar'
@ -108,12 +108,12 @@ class TestParameters(unittest.TestCase):
def test_module_not_fqdn_name(self): def test_module_not_fqdn_name(self):
args = dict( args = dict(
name='foo.baz', name='foo',
lb_method='round-robin' lb_method='round-robin'
) )
with pytest.raises(F5ModuleError) as excinfo: with pytest.raises(F5ModuleError) as excinfo:
p = ModuleParameters(params=args) p = ModuleParameters(params=args)
assert p.name == 'foo.baz' assert p.name == 'foo'
assert 'The provided name must be a valid FQDN' in str(excinfo) assert 'The provided name must be a valid FQDN' in str(excinfo)