mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-05-31 13:29:08 -07:00
Primarily, this patch contains refactors to remove tracebacks that are generated when libraries are missing. There is also, * Removed re-def of cleanup_tokens. * Changed parameter args to be keywords. * Changed imports to include new module_util locations. * Imports also include developing (sideband) module_util locations. * Changed to using F5Client and plain AnsibleModule to prevent tracebacks caused by missing libraries. * Removed init and update methods from most Parameter classes (optimization) as its now included in module_utils. * Changed module and module param references to take into account the new self.module arg. Minor bug fixes made during this refactor.
827 lines
27 KiB
Python
827 lines
27 KiB
Python
#!/usr/bin/python
|
|
# -*- 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
|
|
|
|
|
|
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
|
'status': ['preview'],
|
|
'supported_by': 'community'}
|
|
|
|
DOCUMENTATION = r'''
|
|
---
|
|
module: bigip_gtm_server
|
|
short_description: Manages F5 BIG-IP GTM servers
|
|
description:
|
|
- Manage BIG-IP server configuration. This module is able to manipulate the server
|
|
definitions in a BIG-IP.
|
|
version_added: "2.5"
|
|
options:
|
|
name:
|
|
description:
|
|
- The name of the server.
|
|
required: True
|
|
state:
|
|
description:
|
|
- The server state. If C(absent), an attempt to delete the server will be made.
|
|
This will only succeed if this server is not in use by a virtual server.
|
|
C(present) creates the server and enables it. If C(enabled), enable the server
|
|
if it exists. If C(disabled), create the server if needed, and set state to
|
|
C(disabled).
|
|
default: present
|
|
choices:
|
|
- present
|
|
- absent
|
|
- enabled
|
|
- disabled
|
|
datacenter:
|
|
description:
|
|
- Data center the server belongs to. When creating a new GTM server, this value
|
|
is required.
|
|
devices:
|
|
description:
|
|
- Lists the self IP addresses and translations for each device. When creating a
|
|
new GTM server, this value is required. This list is a complex list that
|
|
specifies a number of keys. There are several supported keys.
|
|
- The C(name) key specifies a name for the device. The device name must
|
|
be unique per server. This key is required.
|
|
- The C(address) key contains an IP address, or list of IP addresses, for the
|
|
destination server. This key is required.
|
|
- The C(translation) key contains an IP address to translate the C(address)
|
|
value above to. This key is optional.
|
|
- Specifying duplicate C(name) fields is a supported means of providing device
|
|
addresses. In this scenario, the addresses will be assigned to the C(name)'s list
|
|
of addresses.
|
|
server_type:
|
|
description:
|
|
- Specifies the server type. The server type determines the metrics that the
|
|
system can collect from the server. When creating a new GTM server, the default
|
|
value C(bigip) is used.
|
|
choices:
|
|
- alteon-ace-director
|
|
- cisco-css
|
|
- cisco-server-load-balancer
|
|
- generic-host
|
|
- radware-wsd
|
|
- windows-nt-4.0
|
|
- bigip
|
|
- cisco-local-director-v2
|
|
- extreme
|
|
- generic-load-balancer
|
|
- sun-solaris
|
|
- cacheflow
|
|
- cisco-local-director-v3
|
|
- foundry-server-iron
|
|
- netapp
|
|
- windows-2000-server
|
|
link_discovery:
|
|
description:
|
|
- Specifies whether the system auto-discovers the links for this server. When
|
|
creating a new GTM server, if this parameter is not specified, the default
|
|
value C(disabled) is used.
|
|
- If you set this parameter to C(enabled) or C(enabled-no-delete), you must
|
|
also ensure that the C(virtual_server_discovery) parameter is also set to
|
|
C(enabled) or C(enabled-no-delete).
|
|
choices:
|
|
- enabled
|
|
- disabled
|
|
- enabled-no-delete
|
|
virtual_server_discovery:
|
|
description:
|
|
- Specifies whether the system auto-discovers the virtual servers for this server.
|
|
When creating a new GTM server, if this parameter is not specified, the default
|
|
value C(disabled) is used.
|
|
choices:
|
|
- enabled
|
|
- disabled
|
|
- enabled-no-delete
|
|
partition:
|
|
description:
|
|
- Device partition to manage resources on.
|
|
default: Common
|
|
version_added: 2.5
|
|
extends_documentation_fragment: f5
|
|
author:
|
|
- Robert Teller
|
|
- Tim Rupp (@caphrim007)
|
|
'''
|
|
|
|
EXAMPLES = r'''
|
|
- name: Create server "GTM_Server"
|
|
bigip_gtm_server:
|
|
server: lb.mydomain.com
|
|
user: admin
|
|
password: secret
|
|
name: GTM_Server
|
|
datacenter: /Common/New York
|
|
server_type: bigip
|
|
link_discovery: disabled
|
|
virtual_server_discovery: disabled
|
|
devices:
|
|
- {'name': 'server_1', 'address': '1.1.1.1'}
|
|
- {'name': 'server_2', 'address': '2.2.2.1', 'translation':'192.168.2.1'}
|
|
- {'name': 'server_2', 'address': '2.2.2.2'}
|
|
- {'name': 'server_3', 'addresses': [{'address':'3.3.3.1'},{'address':'3.3.3.2'}]}
|
|
- {'name': 'server_4', 'addresses': [{'address':'4.4.4.1','translation':'192.168.14.1'}, {'address':'4.4.4.2'}]}
|
|
delegate_to: localhost
|
|
|
|
- name: Create server "GTM_Server" with expanded keys
|
|
bigip_gtm_server:
|
|
server: lb.mydomain.com
|
|
user: admin
|
|
password: secret
|
|
name: GTM_Server
|
|
datacenter: /Common/New York
|
|
server_type: bigip
|
|
link_discovery: disabled
|
|
virtual_server_discovery: disabled
|
|
devices:
|
|
- name: server_1
|
|
address: 1.1.1.1
|
|
- name: server_2
|
|
address: 2.2.2.1
|
|
translation: 192.168.2.1
|
|
- name: server_2
|
|
address: 2.2.2.2
|
|
- name: server_3
|
|
addresses:
|
|
- address: 3.3.3.1
|
|
- address: 3.3.3.2
|
|
- name: server_4
|
|
addresses:
|
|
- address: 4.4.4.1
|
|
translation: 192.168.14.1
|
|
- address: 4.4.4.2
|
|
delegate_to: localhost
|
|
'''
|
|
|
|
RETURN = r'''
|
|
link_discovery:
|
|
description: The new C(link_discovery) configured on the remote device.
|
|
returned: changed
|
|
type: string
|
|
sample: enabled
|
|
virtual_server_discovery:
|
|
description: The new C(virtual_server_discovery) name for the trap destination.
|
|
returned: changed
|
|
type: string
|
|
sample: disabled
|
|
server_type:
|
|
description: The new type of the server.
|
|
returned: changed
|
|
type: string
|
|
sample: bigip
|
|
datacenter:
|
|
description: The new C(datacenter) which the server is part of.
|
|
returned: changed
|
|
type: string
|
|
sample: datacenter01
|
|
'''
|
|
|
|
from distutils.version import LooseVersion
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
from ansible.module_utils.basic import env_fallback
|
|
|
|
HAS_DEVEL_IMPORTS = False
|
|
|
|
try:
|
|
# Sideband repository used for dev
|
|
from library.module_utils.network.f5.bigip import HAS_F5SDK
|
|
from library.module_utils.network.f5.bigip import F5Client
|
|
from library.module_utils.network.f5.common import F5ModuleError
|
|
from library.module_utils.network.f5.common import AnsibleF5Parameters
|
|
from library.module_utils.network.f5.common import cleanup_tokens
|
|
from library.module_utils.network.f5.common import fqdn_name
|
|
from library.module_utils.network.f5.common import f5_argument_spec
|
|
try:
|
|
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
|
|
except ImportError:
|
|
HAS_F5SDK = False
|
|
HAS_DEVEL_IMPORTS = True
|
|
except ImportError:
|
|
# Upstream Ansible
|
|
from ansible.module_utils.network.f5.bigip import HAS_F5SDK
|
|
from ansible.module_utils.network.f5.bigip import F5Client
|
|
from ansible.module_utils.network.f5.common import F5ModuleError
|
|
from ansible.module_utils.network.f5.common import AnsibleF5Parameters
|
|
from ansible.module_utils.network.f5.common import cleanup_tokens
|
|
from ansible.module_utils.network.f5.common import fqdn_name
|
|
from ansible.module_utils.network.f5.common import f5_argument_spec
|
|
try:
|
|
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
|
|
except ImportError:
|
|
HAS_F5SDK = False
|
|
|
|
try:
|
|
from collections import OrderedDict
|
|
except ImportError:
|
|
try:
|
|
from ordereddict import OrderedDict
|
|
except ImportError:
|
|
pass
|
|
|
|
|
|
class Parameters(AnsibleF5Parameters):
|
|
api_map = {
|
|
'product': 'server_type',
|
|
'virtualServerDiscovery': 'virtual_server_discovery',
|
|
'linkDiscovery': 'link_discovery',
|
|
'addresses': 'devices'
|
|
}
|
|
|
|
updatables = [
|
|
'link_discovery', 'virtual_server_discovery', 'server_type_and_devices',
|
|
'datacenter', 'state'
|
|
]
|
|
|
|
returnables = [
|
|
'link_discovery', 'virtual_server_discovery', 'server_type', 'datacenter',
|
|
'enabled'
|
|
]
|
|
|
|
api_attributes = [
|
|
'linkDiscovery', 'virtualServerDiscovery', 'product', 'addresses',
|
|
'datacenter', 'enabled', 'disabled'
|
|
]
|
|
|
|
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):
|
|
@property
|
|
def devices(self):
|
|
if self._values['devices'] is None:
|
|
return None
|
|
return self._values['devices']
|
|
|
|
@property
|
|
def server_type(self):
|
|
if self._values['server_type'] is None:
|
|
return None
|
|
elif self._values['server_type'] in ['single-bigip', 'redundant-bigip']:
|
|
return 'bigip'
|
|
else:
|
|
return self._values['server_type']
|
|
|
|
@property
|
|
def raw_server_type(self):
|
|
if self._values['server_type'] is None:
|
|
return None
|
|
return self._values['server_type']
|
|
|
|
@property
|
|
def enabled(self):
|
|
if self._values['enabled'] is None:
|
|
return None
|
|
return True
|
|
|
|
@property
|
|
def disabled(self):
|
|
if self._values['disabled'] is None:
|
|
return None
|
|
return True
|
|
|
|
|
|
class ModuleParameters(Parameters):
|
|
@property
|
|
def devices(self):
|
|
if self._values['devices'] is None:
|
|
return None
|
|
result = []
|
|
|
|
for device in self._values['devices']:
|
|
if not any(x for x in ['address', 'addresses'] if x in device):
|
|
raise F5ModuleError(
|
|
"The specified device list must contain an 'address' or 'addresses' key"
|
|
)
|
|
|
|
if 'address' in device:
|
|
translation = self._determine_translation(device)
|
|
name = device['address']
|
|
device_name = device['name']
|
|
result.append({
|
|
'name': name,
|
|
'deviceName': device_name,
|
|
'translation': translation
|
|
})
|
|
elif 'addresses' in device:
|
|
for address in device['addresses']:
|
|
translation = self._determine_translation(address)
|
|
name = address['address']
|
|
device_name = device['name']
|
|
result.append({
|
|
'name': name,
|
|
'deviceName': device_name,
|
|
'translation': translation
|
|
})
|
|
return result
|
|
|
|
def devices_list(self):
|
|
if self._values['devices'] is None:
|
|
return None
|
|
return self._values['devices']
|
|
|
|
@property
|
|
def enabled(self):
|
|
if self._values['state'] in ['present', 'enabled']:
|
|
return True
|
|
return False
|
|
|
|
@property
|
|
def datacenter(self):
|
|
if self._values['datacenter'] is None:
|
|
return None
|
|
return self._fqdn_name(self._values['datacenter'])
|
|
|
|
def _determine_translation(self, device):
|
|
if 'translation' not in device:
|
|
return 'none'
|
|
return device['translation']
|
|
|
|
@property
|
|
def state(self):
|
|
if self._values['state'] == 'enabled':
|
|
return 'present'
|
|
return self._values['state']
|
|
|
|
|
|
class Changes(Parameters):
|
|
def to_return(self):
|
|
result = {}
|
|
for returnable in self.returnables:
|
|
result[returnable] = getattr(self, returnable)
|
|
result = self._filter_params(result)
|
|
return result
|
|
|
|
|
|
class UsableChanges(Changes):
|
|
pass
|
|
|
|
|
|
class ReportableChanges(Changes):
|
|
@property
|
|
def server_type(self):
|
|
if self._values['server_type'] in ['single-bigip', 'redundant-bigip']:
|
|
return 'bigip'
|
|
return self._values['server_type']
|
|
|
|
|
|
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
|
|
|
|
def _discovery_constraints(self):
|
|
if self.want.virtual_server_discovery is None:
|
|
virtual_server_discovery = self.have.virtual_server_discovery
|
|
else:
|
|
virtual_server_discovery = self.want.virtual_server_discovery
|
|
|
|
if self.want.link_discovery is None:
|
|
link_discovery = self.have.link_discovery
|
|
else:
|
|
link_discovery = self.want.link_discovery
|
|
|
|
if link_discovery in ['enabled', 'enabled-no-delete'] and virtual_server_discovery == 'disabled':
|
|
raise F5ModuleError(
|
|
"Virtual server discovery must be enabled if link discovery is enabled"
|
|
)
|
|
|
|
def _devices_changed(self):
|
|
if self.want.devices is None and self.want.server_type is None:
|
|
return None
|
|
if self.want.devices is None:
|
|
devices = self.have.devices
|
|
else:
|
|
devices = self.want.devices
|
|
if len(devices) == 0:
|
|
raise F5ModuleError(
|
|
"A GTM server must have at least one device associated with it."
|
|
)
|
|
want = [OrderedDict(sorted(d.items())) for d in devices]
|
|
have = [OrderedDict(sorted(d.items())) for d in self.have.devices]
|
|
if want != have:
|
|
return True
|
|
return False
|
|
|
|
def _server_type_changed(self):
|
|
if self.want.server_type is None:
|
|
self.want.update({'server_type': self.have.server_type})
|
|
if self.want.server_type != self.have.server_type:
|
|
return True
|
|
return False
|
|
|
|
@property
|
|
def link_discovery(self):
|
|
self._discovery_constraints()
|
|
if self.want.link_discovery != self.have.link_discovery:
|
|
return self.want.link_discovery
|
|
|
|
@property
|
|
def virtual_server_discovery(self):
|
|
self._discovery_constraints()
|
|
if self.want.virtual_server_discovery != self.have.virtual_server_discovery:
|
|
return self.want.virtual_server_discovery
|
|
|
|
def _handle_current_server_type_and_devices(self, devices_change, server_change):
|
|
result = {}
|
|
if devices_change:
|
|
result['devices'] = self.want.devices
|
|
if server_change:
|
|
result['server_type'] = self.want.server_type
|
|
return result
|
|
|
|
def _handle_legacy_server_type_and_devices(self, devices_change, server_change):
|
|
result = {}
|
|
if server_change and devices_change:
|
|
result['devices'] = self.want.devices
|
|
if len(self.want.devices) > 1 and self.want.server_type == 'bigip':
|
|
if self.have.raw_server_type != 'redundant-bigip':
|
|
result['server_type'] = 'redundant-bigip'
|
|
elif self.want.server_type == 'bigip':
|
|
if self.have.raw_server_type != 'single-bigip':
|
|
result['server_type'] = 'single-bigip'
|
|
else:
|
|
result['server_type'] = self.want.server_type
|
|
|
|
elif devices_change:
|
|
result['devices'] = self.want.devices
|
|
if len(self.want.devices) > 1 and self.have.server_type == 'bigip':
|
|
if self.have.raw_server_type != 'redundant-bigip':
|
|
result['server_type'] = 'redundant-bigip'
|
|
elif self.have.server_type == 'bigip':
|
|
if self.have.raw_server_type != 'single-bigip':
|
|
result['server_type'] = 'single-bigip'
|
|
else:
|
|
result['server_type'] = self.want.server_type
|
|
|
|
elif server_change:
|
|
if len(self.have.devices) > 1 and self.want.server_type == 'bigip':
|
|
if self.have.raw_server_type != 'redundant-bigip':
|
|
result['server_type'] = 'redundant-bigip'
|
|
elif self.want.server_type == 'bigip':
|
|
if self.have.raw_server_type != 'single-bigip':
|
|
result['server_type'] = 'single-bigip'
|
|
else:
|
|
result['server_type'] = self.want.server_type
|
|
return result
|
|
|
|
@property
|
|
def server_type_and_devices(self):
|
|
"""Compares difference between server type and devices list
|
|
|
|
These two parameters are linked with each other and, therefore, must be
|
|
compared together to ensure that the correct setting is sent to BIG-IP
|
|
|
|
:return:
|
|
"""
|
|
devices_change = self._devices_changed()
|
|
server_change = self._server_type_changed()
|
|
if not devices_change and not server_change:
|
|
return None
|
|
tmos_version = self.client.api.tmos_version
|
|
if LooseVersion(tmos_version) >= LooseVersion('13.0.0'):
|
|
result = self._handle_current_server_type_and_devices(
|
|
devices_change, server_change
|
|
)
|
|
return result
|
|
else:
|
|
result = self._handle_legacy_server_type_and_devices(
|
|
devices_change, server_change
|
|
)
|
|
return result
|
|
|
|
@property
|
|
def state(self):
|
|
if self.want.state == 'disabled' and self.have.enabled:
|
|
return dict(disabled=True)
|
|
elif self.want.state in ['present', 'enabled'] and self.have.disabled:
|
|
return dict(enabled=True)
|
|
|
|
|
|
class ModuleManager(object):
|
|
def __init__(self, *args, **kwargs):
|
|
self.module = kwargs.get('module', None)
|
|
self.client = kwargs.get('client', None)
|
|
self.kwargs = kwargs
|
|
|
|
def exec_module(self):
|
|
if not self.gtm_provisioned():
|
|
raise F5ModuleError(
|
|
"GTM must be provisioned to use this module."
|
|
)
|
|
if self.version_is_less_than('13.0.0'):
|
|
manager = self.get_manager('v1')
|
|
else:
|
|
manager = self.get_manager('v2')
|
|
return manager.exec_module()
|
|
|
|
def get_manager(self, type):
|
|
if type == 'v1':
|
|
return V1Manager(**self.kwargs)
|
|
elif type == 'v2':
|
|
return V2Manager(**self.kwargs)
|
|
|
|
def version_is_less_than(self, version):
|
|
tmos_version = self.client.api.tmos_version
|
|
if LooseVersion(tmos_version) < LooseVersion(version):
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def gtm_provisioned(self):
|
|
resource = self.client.api.tm.sys.dbs.db.load(
|
|
name='provisioned.cpu.gtm'
|
|
)
|
|
if int(resource.value) == 0:
|
|
return False
|
|
return True
|
|
|
|
|
|
class BaseManager(object):
|
|
def __init__(self, *args, **kwargs):
|
|
self.module = kwargs.get('module', None)
|
|
self.client = kwargs.get('client', None)
|
|
self.want = ModuleParameters(params=self.module.params)
|
|
self.want.update(dict(client=self.client))
|
|
self.have = ApiParameters()
|
|
self.changes = UsableChanges()
|
|
|
|
def _set_changed_options(self):
|
|
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)
|
|
|
|
def _update_changed_options(self):
|
|
diff = Difference(self.want, self.have)
|
|
diff.client = self.client
|
|
updatables = Parameters.updatables
|
|
changed = dict()
|
|
for k in updatables:
|
|
change = diff.compare(k)
|
|
if change is None:
|
|
continue
|
|
else:
|
|
if isinstance(change, dict):
|
|
changed.update(change)
|
|
else:
|
|
changed[k] = change
|
|
if changed:
|
|
self.changes = UsableChanges(params=changed)
|
|
return True
|
|
return False
|
|
|
|
def exec_module(self):
|
|
changed = False
|
|
result = dict()
|
|
state = self.want.state
|
|
|
|
try:
|
|
if state in ['present', 'enabled', 'disabled']:
|
|
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.module.deprecate(
|
|
msg=warning['msg'],
|
|
version=warning['version']
|
|
)
|
|
|
|
def _check_link_discovery_requirements(self):
|
|
if self.want.link_discovery in ['enabled', 'enabled-no-delete'] and self.want.virtual_server_discovery == 'disabled':
|
|
raise F5ModuleError(
|
|
"Virtual server discovery must be enabled if link discovery is enabled"
|
|
)
|
|
|
|
def present(self):
|
|
if self.exists():
|
|
return self.update()
|
|
else:
|
|
return self.create()
|
|
|
|
def create(self):
|
|
if self.want.state == 'disabled':
|
|
self.want.update({'disabled': True})
|
|
elif self.want.state in ['present', 'enabled']:
|
|
self.want.update({'enabled': True})
|
|
self._set_changed_options()
|
|
|
|
if self.want.devices is None:
|
|
raise F5ModuleError(
|
|
"You must provide an initial device."
|
|
)
|
|
self._assign_creation_defaults()
|
|
|
|
if self.module.check_mode:
|
|
return True
|
|
self.create_on_device()
|
|
if self.exists():
|
|
return True
|
|
else:
|
|
raise F5ModuleError("Failed to create the server")
|
|
|
|
def create_on_device(self):
|
|
params = self.want.api_params()
|
|
self.client.api.tm.gtm.servers.server.create(
|
|
name=self.want.name,
|
|
partition=self.want.partition,
|
|
**params
|
|
)
|
|
|
|
def update(self):
|
|
self.have = self.read_current_from_device()
|
|
if not self.should_update():
|
|
return False
|
|
if self.module.check_mode:
|
|
return True
|
|
self.update_on_device()
|
|
return True
|
|
|
|
def read_current_from_device(self):
|
|
resource = self.client.api.tm.gtm.servers.server.load(
|
|
name=self.want.name,
|
|
partition=self.want.partition
|
|
)
|
|
result = resource.attrs
|
|
return ApiParameters(params=result)
|
|
|
|
def should_update(self):
|
|
result = self._update_changed_options()
|
|
if result:
|
|
return True
|
|
return False
|
|
|
|
def update_on_device(self):
|
|
params = self.changes.api_params()
|
|
resource = self.client.api.tm.gtm.servers.server.load(
|
|
name=self.want.name,
|
|
partition=self.want.partition
|
|
)
|
|
resource.modify(**params)
|
|
|
|
def absent(self):
|
|
changed = False
|
|
if self.exists():
|
|
changed = self.remove()
|
|
return changed
|
|
|
|
def remove(self):
|
|
if self.module.check_mode:
|
|
return True
|
|
self.remove_from_device()
|
|
if self.exists():
|
|
raise F5ModuleError("Failed to delete the server")
|
|
return True
|
|
|
|
def remove_from_device(self):
|
|
resource = self.client.api.tm.gtm.servers.server.load(
|
|
name=self.want.name,
|
|
partition=self.want.partition
|
|
)
|
|
resource.delete()
|
|
|
|
def exists(self):
|
|
result = self.client.api.tm.gtm.servers.server.exists(
|
|
name=self.want.name,
|
|
partition=self.want.partition
|
|
)
|
|
return result
|
|
|
|
|
|
class V1Manager(BaseManager):
|
|
def _assign_creation_defaults(self):
|
|
if self.want.server_type is None:
|
|
if len(self.want.devices) == 0:
|
|
raise F5ModuleError(
|
|
"You must provide at least one device."
|
|
)
|
|
elif len(self.want.devices) == 1:
|
|
self.want.update({'server_type': 'single-bigip'})
|
|
else:
|
|
self.want.update({'server_type': 'redundant-bigip'})
|
|
else:
|
|
if len(self.want.devices) == 1:
|
|
self.want.update({'server_type': 'single-bigip'})
|
|
else:
|
|
self.want.update({'server_type': 'redundant-bigip'})
|
|
if self.want.link_discovery is None:
|
|
self.want.update({'link_discovery': 'disabled'})
|
|
if self.want.virtual_server_discovery is None:
|
|
self.want.update({'virtual_server_discovery': 'disabled'})
|
|
self._check_link_discovery_requirements()
|
|
|
|
|
|
class V2Manager(BaseManager):
|
|
def _assign_creation_defaults(self):
|
|
if self.want.server_type is None:
|
|
self.want.update({'server_type': 'bigip'})
|
|
if self.want.link_discovery is None:
|
|
self.want.update({'link_discovery': 'disabled'})
|
|
if self.want.virtual_server_discovery is None:
|
|
self.want.update({'virtual_server_discovery': 'disabled'})
|
|
self._check_link_discovery_requirements()
|
|
|
|
|
|
class ArgumentSpec(object):
|
|
def __init__(self):
|
|
self.states = ['absent', 'present', 'enabled', 'disabled']
|
|
self.server_types = [
|
|
'alteon-ace-director', 'cisco-css', 'cisco-server-load-balancer',
|
|
'generic-host', 'radware-wsd', 'windows-nt-4.0', 'bigip',
|
|
'cisco-local-director-v2', 'extreme', 'generic-load-balancer',
|
|
'sun-solaris', 'cacheflow', 'cisco-local-director-v3',
|
|
'foundry-server-iron', 'netapp', 'standalone-bigip',
|
|
'redundant-bigip', 'windows-2000-server'
|
|
]
|
|
self.supports_check_mode = True
|
|
argument_spec = dict(
|
|
state=dict(
|
|
default='present',
|
|
choices=self.states,
|
|
),
|
|
name=dict(required=True),
|
|
server_type=dict(
|
|
choices=self.server_types,
|
|
aliases=['product']
|
|
),
|
|
datacenter=dict(),
|
|
link_discovery=dict(
|
|
choices=['enabled', 'disabled', 'enabled-no-delete']
|
|
),
|
|
virtual_server_discovery=dict(
|
|
choices=['enabled', 'disabled', 'enabled-no-delete']
|
|
),
|
|
devices=dict(
|
|
type='list'
|
|
),
|
|
partition=dict(
|
|
default='Common',
|
|
fallback=(env_fallback, ['F5_PARTITION'])
|
|
)
|
|
)
|
|
self.argument_spec = {}
|
|
self.argument_spec.update(f5_argument_spec)
|
|
self.argument_spec.update(argument_spec)
|
|
|
|
|
|
def main():
|
|
spec = ArgumentSpec()
|
|
|
|
module = AnsibleModule(
|
|
argument_spec=spec.argument_spec,
|
|
supports_check_mode=spec.supports_check_mode
|
|
)
|
|
if not HAS_F5SDK:
|
|
module.fail_json(msg="The python f5-sdk module is required")
|
|
|
|
try:
|
|
client = F5Client(**module.params)
|
|
mm = ModuleManager(module=module, client=client)
|
|
results = mm.exec_module()
|
|
cleanup_tokens(client)
|
|
module.exit_json(**results)
|
|
except F5ModuleError as ex:
|
|
cleanup_tokens(client)
|
|
module.fail_json(msg=str(ex))
|
|
|
|
if __name__ == '__main__':
|
|
main()
|