diff options
Diffstat (limited to 'lib/ansible/modules/network/f5/bigip_virtual_server.py')
-rw-r--r-- | lib/ansible/modules/network/f5/bigip_virtual_server.py | 397 |
1 files changed, 101 insertions, 296 deletions
diff --git a/lib/ansible/modules/network/f5/bigip_virtual_server.py b/lib/ansible/modules/network/f5/bigip_virtual_server.py index 7d446233e1..ddccc9116f 100644 --- a/lib/ansible/modules/network/f5/bigip_virtual_server.py +++ b/lib/ansible/modules/network/f5/bigip_virtual_server.py @@ -137,14 +137,6 @@ options: - Default Profile which manages the session persistence. - If you want to remove the existing default persistence profile, specify an empty value; C(""). See the documentation for an example. - route_advertisement_state: - description: - - Enable route advertisement for destination. - - Deprecated in 2.4. Use the C(bigip_virtual_address) module instead. - choices: - - enabled - - disabled - version_added: "2.3" description: description: - Virtual server description. @@ -171,12 +163,9 @@ options: version_added: 2.5 notes: - Requires BIG-IP software version >= 11 - - Requires the f5-sdk Python package on the host. This is as easy as pip - install f5-sdk. - Requires the netaddr Python package on the host. This is as easy as pip install netaddr. requirements: - - f5-sdk - netaddr extends_documentation_fragment: f5 author: @@ -381,18 +370,38 @@ metadata: import re -from ansible.module_utils.f5_utils import AnsibleF5Client -from ansible.module_utils.f5_utils import AnsibleF5Parameters -from ansible.module_utils.f5_utils import HAS_F5SDK -from ansible.module_utils.f5_utils import F5ModuleError +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.basic import env_fallback from ansible.module_utils.six import iteritems -from collections import defaultdict from collections import namedtuple try: - from ansible.module_utils.f5_utils import iControlUnexpectedHTTPError + # Sideband repository used for dev + from library.module_utils.network.f5.bigip import HAS_F5SDK + from library.module_utils.network.f5.bigip import F5Client + from library.module_utils.network.f5.common import F5ModuleError + from library.module_utils.network.f5.common import AnsibleF5Parameters + from library.module_utils.network.f5.common import cleanup_tokens + from library.module_utils.network.f5.common import fqdn_name + from library.module_utils.network.f5.common import f5_argument_spec + try: + from library.module_utils.network.f5.common import iControlUnexpectedHTTPError + except ImportError: + HAS_F5SDK = False + HAS_DEVEL_IMPORTS = True except ImportError: - HAS_F5SDK = False + # Upstream Ansible + from ansible.module_utils.network.f5.bigip import HAS_F5SDK + from ansible.module_utils.network.f5.bigip import F5Client + from ansible.module_utils.network.f5.common import F5ModuleError + from ansible.module_utils.network.f5.common import AnsibleF5Parameters + from ansible.module_utils.network.f5.common import cleanup_tokens + from ansible.module_utils.network.f5.common import fqdn_name + from ansible.module_utils.network.f5.common import f5_argument_spec + try: + from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError + except ImportError: + HAS_F5SDK = False try: import netaddr @@ -402,100 +411,6 @@ except ImportError: class Parameters(AnsibleF5Parameters): - def __init__(self, params=None): - self._values = defaultdict(lambda: None) - if params: - self.update(params=params) - - def update(self, params=None): - if params: - for k, v in iteritems(params): - if self.api_map is not None and k in self.api_map: - map_key = self.api_map[k] - else: - map_key = k - - # Handle weird API parameters like `dns.proxy.__iter__` by - # using a map provided by the module developer - class_attr = getattr(type(self), map_key, None) - if isinstance(class_attr, property): - # There is a mapped value for the api_map key - if class_attr.fset is None: - # If the mapped value does not have - # an associated setter - self._values[map_key] = v - else: - # The mapped value has a setter - setattr(self, map_key, v) - else: - # If the mapped value is not a @property - self._values[map_key] = v - - def to_return(self): - result = {} - for returnable in self.returnables: - try: - result[returnable] = getattr(self, returnable) - except Exception as ex: - pass - result = self._filter_params(result) - return result - - def _fqdn_name(self, value): - if value is not None and not value.startswith('/'): - return '/{0}/{1}'.format(self.partition, value) - return value - - def api_params(self): - result = {} - for api_attribute in self.api_attributes: - if self.api_map is not None and api_attribute in self.api_map: - result[api_attribute] = getattr(self, self.api_map[api_attribute]) - else: - result[api_attribute] = getattr(self, api_attribute) - result = self._filter_params(result) - return result - - -class VirtualAddressParameters(Parameters): - api_map = { - 'routeAdvertisement': 'route_advertisement_state' - } - returnables = [ - 'route_advertisement_state' - ] - - updatables = [ - 'route_advertisement_state' - ] - - api_attributes = [ - 'routeAdvertisement' - ] - - -class VirtualAddressModuleParameters(VirtualAddressParameters): - @property - def route_advertisement_state(self): - # TODO: Remove in 2.5 - if self._values['route_advertisement_state'] is None: - return None - if self._values['__warnings'] is None: - self._values['__warnings'] = [] - self._values['__warnings'].append( - dict( - msg="Usage of the 'route_advertisement_state' parameter is deprecated. Use the bigip_virtual_address module instead", - version='2.4' - ) - ) - return str(self._values['route_advertisement_state']) - - -class VirtualAddressApiParameters(VirtualAddressParameters): - pass - - -class VirtualServerParameters(Parameters): api_map = { 'sourceAddressTranslation': 'snat', 'fallbackPersistence': 'fallback_persistence_profile', @@ -566,12 +481,25 @@ class VirtualServerParameters(Parameters): 'vlans_disabled' ] - def __init__(self, params=None): - super(VirtualServerParameters, self).__init__(params) - self.profiles_mutex = [ - 'sip', 'sipsession', 'iiop', 'rtsp', 'http', 'diameter', - 'diametersession', 'radius', 'ftp', 'tftp', 'dns', 'pptp', 'fix' - ] + profiles_mutex = [ + 'sip', 'sipsession', 'iiop', 'rtsp', 'http', 'diameter', + 'diametersession', 'radius', 'ftp', 'tftp', 'dns', 'pptp', 'fix' + ] + + def to_return(self): + result = {} + for returnable in self.returnables: + try: + result[returnable] = getattr(self, returnable) + except Exception as ex: + pass + result = self._filter_params(result) + return result + + def _fqdn_name(self, value): + if value is not None and not value.startswith('/'): + return '/{0}/{1}'.format(self.partition, value) + return value def is_valid_ip(self, value): try: @@ -618,7 +546,7 @@ class VirtualServerParameters(Parameters): return result -class VirtualServerApiParameters(VirtualServerParameters): +class ApiParameters(Parameters): @property def destination(self): if self._values['destination'] is None: @@ -648,7 +576,7 @@ class VirtualServerApiParameters(VirtualServerParameters): if self._values['destination'] is None: result = Destination(ip=None, port=None, route_domain=None) return result - destination = re.sub(r'^/[a-zA-Z_.-]+/', '', self._values['destination']) + destination = re.sub(r'^/[a-zA-Z0-9_.-]+/', '', self._values['destination']) if self.is_valid_ip(destination): result = Destination( @@ -816,7 +744,7 @@ class VirtualServerApiParameters(VirtualServerParameters): return result -class VirtualServerModuleParameters(VirtualServerParameters): +class ModuleParameters(Parameters): def _handle_profile_context(self, tmp): if 'context' not in tmp: tmp['context'] = 'all' @@ -1106,7 +1034,11 @@ class VirtualServerModuleParameters(VirtualServerParameters): return result -class VirtualServerUsableChanges(VirtualServerParameters): +class Changes(Parameters): + pass + + +class UsableChanges(Changes): @property def vlans(self): if self._values['vlans'] is None: @@ -1118,11 +1050,7 @@ class VirtualServerUsableChanges(VirtualServerParameters): return self._values['vlans'] -class VirtualAddressUsableChanges(VirtualAddressParameters): - pass - - -class VirtualServerReportableChanges(VirtualServerParameters): +class ReportableChanges(Changes): @property def snat(self): if self._values['snat'] is None: @@ -1137,13 +1065,13 @@ class VirtualServerReportableChanges(VirtualServerParameters): @property def destination(self): - params = VirtualServerApiParameters(dict(destination=self._values['destination'])) + params = ApiParameters(params=dict(destination=self._values['destination'])) result = params.destination_tuple.ip return result @property def port(self): - params = VirtualServerApiParameters(dict(destination=self._values['destination'])) + params = ApiParameters(params=dict(destination=self._values['destination'])) result = params.destination_tuple.port return result @@ -1175,10 +1103,6 @@ class VirtualServerReportableChanges(VirtualServerParameters): return self._values['vlans'] -class VirtualAddressReportableChanges(VirtualAddressParameters): - pass - - class Difference(object): def __init__(self, want, have=None): self.have = have @@ -1461,49 +1385,12 @@ class Difference(object): class ModuleManager(object): - def __init__(self, client): - self.client = client - - def exec_module(self): - managers = list() - managers.append(self.get_manager('virtual_server')) - if self.client.module.params['route_advertisement_state'] is not None: - managers.append(self.get_manager('virtual_address')) - result = self.execute_managers(managers) - return result - - def execute_managers(self, managers): - results = dict(changed=False) - for manager in managers: - result = manager.exec_module() - for k, v in iteritems(result): - if k == 'changed': - if v is True: - results['changed'] = True - else: - results[k] = v - return results - - def get_manager(self, type): - vsm = VirtualServerManager(self.client) - if type == 'virtual_server': - return vsm - elif type == 'virtual_address': - self.set_name_of_virtual_address() - result = VirtualAddressManager(self.client) - return result - - def set_name_of_virtual_address(self): - mgr = VirtualServerManager(self.client) - params = mgr.read_current_from_device() - destination = params.destination_tuple - self.client.module.params['name'] = destination.ip - - -class BaseManager(object): - def __init__(self, client): - self.client = client - self.have = None + def __init__(self, *args, **kwargs): + self.module = kwargs.get('module', None) + self.client = kwargs.get('client', None) + self.have = ApiParameters() + self.want = ModuleParameters(client=self.client, params=self.module.params) + self.changes = UsableChanges() def exec_module(self): changed = False @@ -1518,7 +1405,7 @@ class BaseManager(object): except iControlUnexpectedHTTPError as e: raise F5ModuleError(str(e)) - reportable = self.get_reportable_changes() + reportable = ReportableChanges(params=self.changes.to_return()) changes = reportable.to_return() result.update(**changes) result.update(dict(changed=changed)) @@ -1528,7 +1415,7 @@ class BaseManager(object): def _announce_deprecations(self, result): warnings = result.pop('__warnings', []) for warning in warnings: - self.client.module.deprecate( + self.module.deprecate( msg=warning['msg'], version=warning['version'] ) @@ -1548,23 +1435,11 @@ class BaseManager(object): self.have = self.read_current_from_device() if not self.should_update(): return False - if self.client.check_mode: + if self.module.check_mode: return True self.update_on_device() return True - def create(self): - if self.client.check_mode: - return True - - # This must be changed back to a list to make a valid REST API - # value. The module manipulates this as a normal dictionary - if self.want.default_persistence_profile is not None: - self.want.update({'default_persistence_profile': [self.want.default_persistence_profile]}) - - self.create_on_device() - return True - def should_update(self): result = self._update_changed_options() if result: @@ -1572,36 +1447,28 @@ class BaseManager(object): return False def remove(self): - if self.client.check_mode: + if self.module.check_mode: return True self.remove_from_device() if self.exists(): raise F5ModuleError("Failed to delete the resource") return True - -class VirtualServerManager(BaseManager): - def __init__(self, client): - super(VirtualServerManager, self).__init__(client) - self.have = None - self.want = VirtualServerModuleParameters(self.client.module.params) - self.changes = VirtualServerUsableChanges() - def get_reportable_changes(self): - result = VirtualServerReportableChanges(self.changes.to_return()) + result = ReportableChanges(params=self.changes.to_return()) return result def _set_changed_options(self): changed = {} - for key in VirtualServerParameters.returnables: + for key in Parameters.returnables: if getattr(self.want, key) is not None: changed[key] = getattr(self.want, key) if changed: - self.changes = VirtualServerUsableChanges(changed) + self.changes = UsableChanges(params=changed) def _update_changed_options(self): diff = Difference(self.want, self.have) - updatables = VirtualServerParameters.updatables + updatables = Parameters.updatables changed = dict() for k in updatables: change = diff.compare(k) @@ -1613,7 +1480,7 @@ class VirtualServerManager(BaseManager): else: changed[k] = change if changed: - self.changes = VirtualServerUsableChanges(changed) + self.changes = UsableChanges(params=changed) return True return False @@ -1628,6 +1495,11 @@ class VirtualServerManager(BaseManager): required_resources = ['destination', 'port'] self._set_changed_options() + # This must be changed back to a list to make a valid REST API + # value. The module manipulates this as a normal dictionary + if self.want.default_persistence_profile is not None: + self.want.update({'default_persistence_profile': [self.want.default_persistence_profile]}) + if self.want.destination is None: raise F5ModuleError( "'destination' must be specified when creating a virtual server" @@ -1652,7 +1524,10 @@ class VirtualServerManager(BaseManager): raise F5ModuleError( "The source and destination addresses for the virtual server must be be the same type (IPv4 or IPv6)." ) - return super(VirtualServerManager, self).create() + if self.module.check_mode: + return True + self.create_on_device() + return True def update_on_device(self): params = self.changes.api_params() @@ -1674,7 +1549,7 @@ class VirtualServerManager(BaseManager): ) params = result.attrs params.update(dict(kind=result.to_dict().get('kind', None))) - result = VirtualServerApiParameters(params) + result = ApiParameters(params=params) return result def create_on_device(self): @@ -1694,71 +1569,10 @@ class VirtualServerManager(BaseManager): resource.delete() -class VirtualAddressManager(BaseManager): - def __init__(self, client): - super(VirtualAddressManager, self).__init__(client) - self.want = VirtualAddressModuleParameters(self.client.module.params) - self.have = VirtualAddressApiParameters() - self.changes = VirtualAddressUsableChanges() - - def get_reportable_changes(self): - result = VirtualAddressReportableChanges(self.changes.to_return()) - return result - - def _set_changed_options(self): - changed = {} - for key in VirtualAddressParameters.returnables: - if getattr(self.want, key) is not None: - changed[key] = getattr(self.want, key) - if changed: - self.changes = VirtualAddressUsableChanges(changed) - - def _update_changed_options(self): - diff = Difference(self.want, self.have) - updatables = VirtualAddressParameters.updatables - changed = dict() - for k in updatables: - change = diff.compare(k) - if change is None: - continue - else: - if isinstance(change, dict): - changed.update(change) - else: - changed[k] = change - if changed: - self.changes = VirtualAddressUsableChanges(changed) - return True - return False - - def read_current_from_device(self): - result = self.client.api.tm.ltm.virtual_address_s.virtual_address.load( - name=self.want.name, - partition=self.want.partition - ) - result = VirtualAddressParameters(result.attrs) - return result - - def update_on_device(self): - params = self.want.api_params() - resource = self.client.api.tm.ltm.virtual_address_s.virtual_address.load( - name=self.want.name, - partition=self.want.partition - ) - resource.modify(**params) - - def exists(self): - result = self.client.api.tm.ltm.virtual_address_s.virtual_address.exists( - name=self.want.name, - partition=self.want.partition - ) - return result - - class ArgumentSpec(object): def __init__(self): self.supports_check_mode = True - self.argument_spec = dict( + argument_spec = dict( state=dict( default='present', choices=['present', 'absent', 'disabled', 'enabled'] @@ -1798,54 +1612,45 @@ class ArgumentSpec(object): pool=dict(), description=dict(), snat=dict(), - route_advertisement_state=dict( - choices=['enabled', 'disabled'] - ), default_persistence_profile=dict(), fallback_persistence_profile=dict(), source=dict(), - metadata=dict(type='raw') + metadata=dict(type='raw'), + partition=dict( + default='Common', + fallback=(env_fallback, ['F5_PARTITION']) + ) ) - self.f5_product_name = 'bigip' + self.argument_spec = {} + self.argument_spec.update(f5_argument_spec) + self.argument_spec.update(argument_spec) self.mutually_exclusive = [ ['enabled_vlans', 'disabled_vlans'] ] -def cleanup_tokens(client): - try: - resource = client.api.shared.authz.tokens_s.token.load( - name=client.api.icrs.token - ) - resource.delete() - except Exception: - pass - - def main(): - if not HAS_F5SDK: - raise F5ModuleError("The python f5-sdk module is required") - - if not HAS_NETADDR: - raise F5ModuleError("The python netaddr module is required") - spec = ArgumentSpec() - client = AnsibleF5Client( + module = AnsibleModule( argument_spec=spec.argument_spec, supports_check_mode=spec.supports_check_mode, - f5_product_name=spec.f5_product_name, mutually_exclusive=spec.mutually_exclusive ) + if not HAS_F5SDK: + module.fail_json(msg="The python f5-sdk module is required") + if not HAS_NETADDR: + module.fail_json(msg="The python netaddr module is required") try: - mm = ModuleManager(client) + client = F5Client(**module.params) + mm = ModuleManager(module=module, client=client) results = mm.exec_module() cleanup_tokens(client) - client.module.exit_json(**results) - except F5ModuleError as e: + module.exit_json(**results) + except F5ModuleError as ex: cleanup_tokens(client) - client.module.fail_json(msg=str(e)) + module.fail_json(msg=str(ex)) if __name__ == '__main__': |