diff --git a/lib/ansible/modules/network/f5/bigip_gtm_wide_ip.py b/lib/ansible/modules/network/f5/bigip_gtm_wide_ip.py index 587fe58b11..a6279154c9 100644 --- a/lib/ansible/modules/network/f5/bigip_gtm_wide_ip.py +++ b/lib/ansible/modules/network/f5/bigip_gtm_wide_ip.py @@ -17,13 +17,15 @@ module: bigip_gtm_wide_ip short_description: Manages F5 BIG-IP GTM wide ip description: - Manages F5 BIG-IP GTM wide ip. -version_added: "2.0" +version_added: 2.0 options: pool_lb_method: description: - Specifies the load balancing method used to select a pool in this wide IP. This setting is relevant only when multiple pools are configured 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 aliases: ['lb_method'] choices: @@ -31,6 +33,8 @@ options: - ratio - topology - global-availability + - global_availability + - round_robin version_added: 2.5 name: description: @@ -80,13 +84,19 @@ options: suboptions: name: description: - - The name of the pool to include - required: true + - The name of the pool to include. + required: True ratio: description: - Ratio for the pool. - The system uses this number with the Ratio load balancing method. 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 author: - Tim Rupp (@caphrim007) @@ -98,9 +108,54 @@ EXAMPLES = r''' server: lb.mydomain.com user: admin password: secret - lb_method: round-robin + pool_lb_method: round-robin name: my-wide-ip.example.com 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''' @@ -114,40 +169,40 @@ state: returned: changed type: string 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 env_fallback - -HAS_DEVEL_IMPORTS = False +from ansible.module_utils.six import iteritems +from distutils.version import LooseVersion 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 fq_name + from library.module_utils.network.f5.common import is_valid_fqdn 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 fq_name + from ansible.module_utils.network.f5.common import is_valid_fqdn from ansible.module_utils.network.f5.common import f5_argument_spec try: from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError @@ -157,16 +212,21 @@ except ImportError: class Parameters(AnsibleF5Parameters): 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): - if value is not None and not value.startswith('/'): - return '/{0}/{1}'.format(self.partition, value) - return value + updatables = [ + 'pool_lb_method', 'state', 'pools', 'irules', 'enabled', 'disabled' + ] + + returnables = [ + 'name', 'pool_lb_method', 'state', 'pools', 'irules' + ] + + api_attributes = [ + 'poolLbMode', 'enabled', 'disabled', 'pools', 'rules' + ] class ApiParameters(Parameters): @@ -204,25 +264,15 @@ class ApiParameters(Parameters): class ModuleParameters(Parameters): @property def pool_lb_method(self): - deprecated = [ - '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: + if self._values['pool_lb_method'] is None: return None - lb_method = str(self._values['lb_method']) - if lb_method in deprecated: - raise F5ModuleError( - "The provided lb_method is not supported" - ) - elif lb_method == 'global_availability': + lb_method = str(self._values['pool_lb_method']) + if lb_method == 'global_availability': if self._values['__warnings'] is None: self._values['__warnings'] = [] self._values['__warnings'].append( dict( - msg='The provided lb_method is deprecated', + msg='The provided pool_lb_method is deprecated', version='2.4' ) ) @@ -232,7 +282,7 @@ class ModuleParameters(Parameters): self._values['__warnings'] = [] self._values['__warnings'].append( dict( - msg='The provided lb_method is deprecated', + msg='The provided pool_lb_method is deprecated', version='2.4' ) ) @@ -249,7 +299,7 @@ class ModuleParameters(Parameters): def name(self): if self._values['name'] is None: return None - if not re.search(r'.*\..*\..*', self._values['name']): + if not is_valid_fqdn(self._values['name']): raise F5ModuleError( "The provided name must be a valid FQDN" ) @@ -286,12 +336,28 @@ class ModuleParameters(Parameters): return None for item in self._values['pools']: 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: pool['ratio'] = item['ratio'] - pool['name'] = self._fqdn_name(item['name']) + pool['name'] = fq_name(self.partition, item['name']) result.append(pool) 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): def to_return(self): @@ -310,7 +376,13 @@ class Changes(Parameters): 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): @@ -375,6 +447,17 @@ class Difference(object): result = self._diff_complex_items(self.want.pools, self.have.pools) 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): def __init__(self, *args, **kwargs): @@ -466,16 +549,16 @@ class BaseManager(object): ) 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(): return self.update() else: return self.create() 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() if self.module.check_mode: return True @@ -519,7 +602,7 @@ class UntypedManager(BaseManager): ) def update_on_device(self): - params = self.want.api_params() + params = self.changes.api_params() result = self.client.api.tm.gtm.wideips.wipeip.load( name=self.want.name, partition=self.want.partition @@ -535,7 +618,7 @@ class UntypedManager(BaseManager): return ApiParameters(params=result) def create_on_device(self): - params = self.want.api_params() + params = self.changes.api_params() self.client.api.tm.gtm.wideips.wideip.create( name=self.want.name, partition=self.want.partition, @@ -580,7 +663,7 @@ class TypedManager(BaseManager): return result def update_on_device(self): - params = self.want.api_params() + params = self.changes.api_params() wideips = self.client.api.tm.gtm.wideips collection = getattr(wideips, self.collection) resource = getattr(collection, self.want.type) @@ -602,7 +685,7 @@ class TypedManager(BaseManager): return ApiParameters(params=result) def create_on_device(self): - params = self.want.api_params() + params = self.changes.api_params() wideips = self.client.api.tm.gtm.wideips collection = getattr(wideips, self.collection) resource = getattr(collection, self.want.type) @@ -626,16 +709,12 @@ class TypedManager(BaseManager): class ArgumentSpec(object): def __init__(self): - deprecated = [ - 'return_to_dns', 'null', 'round_robin', 'static_persist', - 'global_availability', 'vs_capacity', 'least_conn', 'lowest_rtt', - 'lowest_hops', 'packet_rate', 'cpu', 'hit_ratio', 'qos', 'bps', - 'drop_packet', 'explicit_ip', 'connection_rate', 'vs_score' + lb_method_choices = [ + 'round-robin', 'topology', 'ratio', 'global-availability', + + # TODO(Remove in Ansible 2.9) + 'round_robin', 'global_availability' ] - supported = [ - 'round-robin', 'topology', 'ratio', 'global-availability' - ] - lb_method_choices = deprecated + supported self.supports_check_mode = True argument_spec = dict( pool_lb_method=dict( @@ -665,7 +744,10 @@ class ArgumentSpec(object): partition=dict( default='Common', fallback=(env_fallback, ['F5_PARTITION']) - ) + ), + irules=dict( + type='list', + ), ) self.argument_spec = {} self.argument_spec.update(f5_argument_spec) diff --git a/test/sanity/validate-modules/ignore.txt b/test/sanity/validate-modules/ignore.txt index 3ca17b3da6..946e777d8e 100644 --- a/test/sanity/validate-modules/ignore.txt +++ b/test/sanity/validate-modules/ignore.txt @@ -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 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 E325 lib/ansible/modules/network/f5/bigip_monitor_snmp_dca.py E326 diff --git a/test/units/modules/network/f5/test_bigip_gtm_wide_ip.py b/test/units/modules/network/f5/test_bigip_gtm_wide_ip.py index 8bbee73bce..1f80910f00 100644 --- a/test/units/modules/network/f5/test_bigip_gtm_wide_ip.py +++ b/test/units/modules/network/f5/test_bigip_gtm_wide_ip.py @@ -21,12 +21,12 @@ from ansible.compat.tests.mock import patch from ansible.module_utils.basic import AnsibleModule try: - from library.bigip_gtm_wide_ip import ApiParameters - from library.bigip_gtm_wide_ip import ModuleParameters - from library.bigip_gtm_wide_ip import ModuleManager - from library.bigip_gtm_wide_ip import ArgumentSpec - from library.bigip_gtm_wide_ip import UntypedManager - from library.bigip_gtm_wide_ip import TypedManager + from library.modules.bigip_gtm_wide_ip import ApiParameters + from library.modules.bigip_gtm_wide_ip import ModuleParameters + from library.modules.bigip_gtm_wide_ip import ModuleManager + from library.modules.bigip_gtm_wide_ip import ArgumentSpec + from library.modules.bigip_gtm_wide_ip import UntypedManager + 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 iControlUnexpectedHTTPError from test.unit.modules.utils import set_module_args @@ -70,7 +70,7 @@ class TestParameters(unittest.TestCase): def test_module_parameters(self): args = dict( name='foo.baz.bar', - lb_method='round-robin', + pool_lb_method='round-robin', ) p = ModuleParameters(params=args) assert p.name == 'foo.baz.bar' @@ -108,12 +108,12 @@ class TestParameters(unittest.TestCase): def test_module_not_fqdn_name(self): args = dict( - name='foo.baz', + name='foo', lb_method='round-robin' ) with pytest.raises(F5ModuleError) as excinfo: 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)