2020-03-09 10:11:07 +01:00
|
|
|
#!/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': ['deprecated'],
|
|
|
|
'supported_by': 'certified'}
|
|
|
|
|
|
|
|
DOCUMENTATION = r'''
|
|
|
|
---
|
|
|
|
module: bigip_gtm_facts
|
|
|
|
short_description: Collect facts from F5 BIG-IP GTM devices
|
|
|
|
description:
|
|
|
|
- Collect facts from F5 BIG-IP GTM devices.
|
|
|
|
options:
|
|
|
|
include:
|
|
|
|
description:
|
|
|
|
- Fact category to collect.
|
|
|
|
required: True
|
|
|
|
choices:
|
|
|
|
- pool
|
|
|
|
- wide_ip
|
|
|
|
- server
|
|
|
|
filter:
|
|
|
|
description:
|
|
|
|
- Perform regex filter of response. Filtering is done on the name of
|
|
|
|
the resource. Valid filters are anything that can be provided to
|
|
|
|
Python's C(re) module.
|
|
|
|
deprecated:
|
|
|
|
removed_in: '2.11'
|
|
|
|
alternative: bigip_device_info
|
|
|
|
why: >
|
|
|
|
The bigip_gtm_facts module is an outlier as all facts are being collected
|
|
|
|
in the bigip_device_info module. Additionally, the M(bigip_device_info)
|
|
|
|
module is easier to maintain and use.
|
|
|
|
extends_documentation_fragment:
|
|
|
|
- f5networks.f5_modules.f5
|
|
|
|
|
|
|
|
notes:
|
|
|
|
- This module is deprecated. Use the C(bigip_device_info) module instead.
|
|
|
|
author:
|
|
|
|
- Tim Rupp (@caphrim007)
|
|
|
|
'''
|
|
|
|
|
|
|
|
EXAMPLES = r'''
|
|
|
|
- name: Get pool facts
|
|
|
|
bigip_gtm_facts:
|
|
|
|
server: lb.mydomain.com
|
|
|
|
user: admin
|
|
|
|
password: secret
|
|
|
|
include: pool
|
|
|
|
filter: my_pool
|
|
|
|
delegate_to: localhost
|
|
|
|
'''
|
|
|
|
|
|
|
|
RETURN = r'''
|
|
|
|
wide_ip:
|
|
|
|
description:
|
|
|
|
Contains the lb method for the wide ip and the pools that are within the wide ip.
|
|
|
|
returned: changed
|
|
|
|
type: list
|
|
|
|
sample:
|
|
|
|
wide_ip:
|
|
|
|
- enabled: True
|
|
|
|
failure_rcode: noerror
|
|
|
|
failure_rcode_response: disabled
|
|
|
|
failure_rcode_ttl: 0
|
|
|
|
full_path: /Common/foo.ok.com
|
|
|
|
last_resort_pool: ""
|
|
|
|
minimal_response: enabled
|
|
|
|
name: foo.ok.com
|
|
|
|
partition: Common
|
|
|
|
persist_cidr_ipv4: 32
|
|
|
|
persist_cidr_ipv6: 128
|
|
|
|
persistence: disabled
|
|
|
|
pool_lb_mode: round-robin
|
|
|
|
pools:
|
|
|
|
- name: d3qw
|
|
|
|
order: 0
|
|
|
|
partition: Common
|
|
|
|
ratio: 1
|
|
|
|
ttl_persistence: 3600
|
|
|
|
type: naptr
|
|
|
|
pool:
|
|
|
|
description: Contains the pool object status and enabled status.
|
|
|
|
returned: changed
|
|
|
|
type: list
|
|
|
|
sample:
|
|
|
|
pool:
|
|
|
|
- alternate_mode: round-robin
|
|
|
|
dynamic_ratio: disabled
|
|
|
|
enabled: True
|
|
|
|
fallback_mode: return-to-dns
|
|
|
|
full_path: /Common/d3qw
|
|
|
|
load_balancing_mode: round-robin
|
|
|
|
manual_resume: disabled
|
|
|
|
max_answers_returned: 1
|
|
|
|
members:
|
|
|
|
- disabled: True
|
|
|
|
flags: a
|
|
|
|
full_path: ok3.com
|
|
|
|
member_order: 0
|
|
|
|
name: ok3.com
|
|
|
|
order: 10
|
|
|
|
preference: 10
|
|
|
|
ratio: 1
|
|
|
|
service: 80
|
|
|
|
name: d3qw
|
|
|
|
partition: Common
|
|
|
|
qos_hit_ratio: 5
|
|
|
|
qos_hops: 0
|
|
|
|
qos_kilobytes_second: 3
|
|
|
|
qos_lcs: 30
|
|
|
|
qos_packet_rate: 1
|
|
|
|
qos_rtt: 50
|
|
|
|
qos_topology: 0
|
|
|
|
qos_vs_capacity: 0
|
|
|
|
qos_vs_score: 0
|
|
|
|
availability_state: offline
|
|
|
|
enabled_state: disabled
|
|
|
|
ttl: 30
|
|
|
|
type: naptr
|
|
|
|
verify_member_availability: disabled
|
|
|
|
server:
|
|
|
|
description:
|
|
|
|
Contains the virtual server enabled and availability status, and address.
|
|
|
|
returned: changed
|
|
|
|
type: list
|
|
|
|
sample:
|
|
|
|
server:
|
|
|
|
- addresses:
|
|
|
|
- device_name: /Common/qweqwe
|
|
|
|
name: 10.10.10.10
|
|
|
|
translation: none
|
|
|
|
datacenter: /Common/xfxgh
|
|
|
|
enabled: True
|
|
|
|
expose_route_domains: no
|
|
|
|
full_path: /Common/qweqwe
|
|
|
|
iq_allow_path: yes
|
|
|
|
iq_allow_service_check: yes
|
|
|
|
iq_allow_snmp: yes
|
|
|
|
limit_cpu_usage: 0
|
|
|
|
limit_cpu_usage_status: disabled
|
|
|
|
limit_max_bps: 0
|
|
|
|
limit_max_bps_status: disabled
|
|
|
|
limit_max_connections: 0
|
|
|
|
limit_max_connections_status: disabled
|
|
|
|
limit_max_pps: 0
|
|
|
|
limit_max_pps_status: disabled
|
|
|
|
limit_mem_avail: 0
|
|
|
|
limit_mem_avail_status: disabled
|
|
|
|
link_discovery: disabled
|
|
|
|
monitor: /Common/bigip
|
|
|
|
name: qweqwe
|
|
|
|
partition: Common
|
|
|
|
product: single-bigip
|
|
|
|
virtual_server_discovery: disabled
|
|
|
|
virtual_servers:
|
|
|
|
- destination: 10.10.10.10:0
|
|
|
|
enabled: True
|
|
|
|
full_path: jsdfhsd
|
|
|
|
limit_max_bps: 0
|
|
|
|
limit_max_bps_status: disabled
|
|
|
|
limit_max_connections: 0
|
|
|
|
limit_max_connections_status: disabled
|
|
|
|
limit_max_pps: 0
|
|
|
|
limit_max_pps_status: disabled
|
|
|
|
name: jsdfhsd
|
|
|
|
translation_address: none
|
|
|
|
translation_port: 0
|
|
|
|
'''
|
|
|
|
|
|
|
|
import re
|
|
|
|
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
|
|
from ansible.module_utils.six import iteritems
|
|
|
|
from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE
|
|
|
|
from distutils.version import LooseVersion
|
|
|
|
|
|
|
|
try:
|
|
|
|
from f5.bigip import ManagementRoot
|
|
|
|
from icontrol.exceptions import iControlUnexpectedHTTPError
|
|
|
|
from f5.utils.responses.handlers import Stats
|
|
|
|
HAS_F5SDK = True
|
|
|
|
except ImportError:
|
|
|
|
HAS_F5SDK = False
|
|
|
|
|
|
|
|
try:
|
|
|
|
from library.module_utils.network.f5.common import F5BaseClient
|
|
|
|
except ImportError:
|
2020-03-24 23:14:53 +01:00
|
|
|
from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5BaseClient
|
2020-03-09 10:11:07 +01:00
|
|
|
|
|
|
|
try:
|
|
|
|
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 f5_argument_spec
|
|
|
|
except ImportError:
|
2020-03-24 23:14:53 +01:00
|
|
|
from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import F5ModuleError
|
|
|
|
from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import AnsibleF5Parameters
|
|
|
|
from ansible_collections.f5networks.f5_modules.plugins.module_utils.common import f5_argument_spec
|
2020-03-09 10:11:07 +01:00
|
|
|
|
|
|
|
|
|
|
|
class F5Client(F5BaseClient):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
super(F5Client, self).__init__(*args, **kwargs)
|
|
|
|
self.provider = self.merge_provider_params()
|
|
|
|
|
|
|
|
@property
|
|
|
|
def api(self):
|
|
|
|
if self._client:
|
|
|
|
return self._client
|
|
|
|
|
|
|
|
try:
|
|
|
|
result = ManagementRoot(
|
|
|
|
self.provider['server'],
|
|
|
|
self.provider['user'],
|
|
|
|
self.provider['password'],
|
|
|
|
port=self.provider['server_port'],
|
|
|
|
verify=self.provider['validate_certs'],
|
|
|
|
token='tmos'
|
|
|
|
)
|
|
|
|
self._client = result
|
|
|
|
return self._client
|
|
|
|
except Exception as ex:
|
|
|
|
error = 'Unable to connect to {0} on port {1}. The reported error was "{2}".'.format(
|
|
|
|
self.provider['server'], self.provider['server_port'], str(ex)
|
|
|
|
)
|
|
|
|
raise F5ModuleError(error)
|
|
|
|
|
|
|
|
|
|
|
|
class BaseManager(object):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.module = kwargs.get('module', None)
|
|
|
|
self.client = kwargs.get('client', None)
|
|
|
|
self.kwargs = kwargs
|
|
|
|
|
|
|
|
self.types = dict(
|
|
|
|
a_s='a',
|
|
|
|
aaaas='aaaa',
|
|
|
|
cnames='cname',
|
|
|
|
mxs='mx',
|
|
|
|
naptrs='naptr',
|
|
|
|
srvs='srv'
|
|
|
|
)
|
|
|
|
|
|
|
|
def filter_matches_name(self, name):
|
|
|
|
if self.want.filter is None:
|
|
|
|
return True
|
|
|
|
matches = re.match(self.want.filter, str(name))
|
|
|
|
if matches:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def version_is_less_than_12(self):
|
|
|
|
version = self.client.api.tmos_version
|
|
|
|
if LooseVersion(version) < LooseVersion('12.0.0'):
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def get_facts_from_collection(self, collection, collection_type=None):
|
|
|
|
results = []
|
|
|
|
for item in collection:
|
|
|
|
if not self.filter_matches_name(item.name):
|
|
|
|
continue
|
|
|
|
facts = self.format_facts(item, collection_type)
|
|
|
|
results.append(facts)
|
|
|
|
return results
|
|
|
|
|
|
|
|
def read_stats_from_device(self, resource):
|
|
|
|
stats = Stats(resource.stats.load())
|
|
|
|
return stats.stat
|
|
|
|
|
|
|
|
|
|
|
|
class UntypedManager(BaseManager):
|
|
|
|
def exec_module(self):
|
|
|
|
results = []
|
|
|
|
facts = self.read_facts()
|
|
|
|
for item in facts:
|
|
|
|
attrs = item.to_return()
|
|
|
|
filtered = [(k, v) for k, v in iteritems(attrs) if self.filter_matches_name(k)]
|
|
|
|
if filtered:
|
|
|
|
results.append(dict(filtered))
|
|
|
|
return results
|
|
|
|
|
|
|
|
|
|
|
|
class TypedManager(BaseManager):
|
|
|
|
def exec_module(self):
|
|
|
|
results = []
|
|
|
|
for collection, type in iteritems(self.types):
|
|
|
|
facts = self.read_facts(collection)
|
|
|
|
if not facts:
|
|
|
|
continue
|
|
|
|
for x in facts:
|
|
|
|
x.update({'type': type})
|
|
|
|
for item in facts:
|
|
|
|
attrs = item.to_return()
|
|
|
|
filtered = [(k, v) for k, v in iteritems(attrs) if self.filter_matches_name(k)]
|
|
|
|
if filtered:
|
|
|
|
results.append(dict(filtered))
|
|
|
|
return results
|
|
|
|
|
|
|
|
|
|
|
|
class Parameters(AnsibleF5Parameters):
|
|
|
|
@property
|
|
|
|
def include(self):
|
|
|
|
requested = self._values['include']
|
|
|
|
valid = ['pool', 'wide_ip', 'server', 'all']
|
|
|
|
|
|
|
|
if any(x for x in requested if x not in valid):
|
|
|
|
raise F5ModuleError(
|
|
|
|
"The valid 'include' choices are {0}".format(', '.join(valid))
|
|
|
|
)
|
|
|
|
|
|
|
|
if 'all' in requested:
|
|
|
|
return ['all']
|
|
|
|
else:
|
|
|
|
return requested
|
|
|
|
|
|
|
|
|
|
|
|
class BaseParameters(Parameters):
|
|
|
|
@property
|
|
|
|
def enabled(self):
|
|
|
|
if self._values['enabled'] is None:
|
|
|
|
return None
|
|
|
|
elif self._values['enabled'] in BOOLEANS_TRUE:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
@property
|
|
|
|
def disabled(self):
|
|
|
|
if self._values['disabled'] is None:
|
|
|
|
return None
|
|
|
|
elif self._values['disabled'] in BOOLEANS_TRUE:
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
|
|
|
def _remove_internal_keywords(self, resource):
|
|
|
|
resource.pop('kind', None)
|
|
|
|
resource.pop('generation', None)
|
|
|
|
resource.pop('selfLink', None)
|
|
|
|
resource.pop('isSubcollection', None)
|
|
|
|
resource.pop('fullPath', None)
|
|
|
|
|
|
|
|
def to_return(self):
|
|
|
|
result = {}
|
|
|
|
for returnable in self.returnables:
|
|
|
|
result[returnable] = getattr(self, returnable)
|
|
|
|
result = self._filter_params(result)
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
class PoolParameters(BaseParameters):
|
|
|
|
api_map = {
|
|
|
|
'alternateMode': 'alternate_mode',
|
|
|
|
'dynamicRatio': 'dynamic_ratio',
|
|
|
|
'fallbackMode': 'fallback_mode',
|
|
|
|
'fullPath': 'full_path',
|
|
|
|
'loadBalancingMode': 'load_balancing_mode',
|
|
|
|
'manualResume': 'manual_resume',
|
|
|
|
'maxAnswersReturned': 'max_answers_returned',
|
|
|
|
'qosHitRatio': 'qos_hit_ratio',
|
|
|
|
'qosHops': 'qos_hops',
|
|
|
|
'qosKilobytesSecond': 'qos_kilobytes_second',
|
|
|
|
'qosLcs': 'qos_lcs',
|
|
|
|
'qosPacketRate': 'qos_packet_rate',
|
|
|
|
'qosRtt': 'qos_rtt',
|
|
|
|
'qosTopology': 'qos_topology',
|
|
|
|
'qosVsCapacity': 'qos_vs_capacity',
|
|
|
|
'qosVsScore': 'qos_vs_score',
|
|
|
|
'verifyMemberAvailability': 'verify_member_availability',
|
|
|
|
'membersReference': 'members'
|
|
|
|
}
|
|
|
|
|
|
|
|
returnables = [
|
|
|
|
'alternate_mode', 'dynamic_ratio', 'enabled', 'disabled', 'fallback_mode',
|
|
|
|
'load_balancing_mode', 'manual_resume', 'max_answers_returned', 'members',
|
|
|
|
'name', 'partition', 'qos_hit_ratio', 'qos_hops', 'qos_kilobytes_second',
|
|
|
|
'qos_lcs', 'qos_packet_rate', 'qos_rtt', 'qos_topology', 'qos_vs_capacity',
|
|
|
|
'qos_vs_score', 'ttl', 'type', 'full_path', 'availability_state',
|
|
|
|
'enabled_state', 'availability_status'
|
|
|
|
]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def max_answers_returned(self):
|
|
|
|
if self._values['max_answers_returned'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['max_answers_returned'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def members(self):
|
|
|
|
result = []
|
|
|
|
if self._values['members'] is None or 'items' not in self._values['members']:
|
|
|
|
return result
|
|
|
|
for item in self._values['members']['items']:
|
|
|
|
self._remove_internal_keywords(item)
|
|
|
|
if 'disabled' in item:
|
|
|
|
if item['disabled'] in BOOLEANS_TRUE:
|
|
|
|
item['disabled'] = True
|
|
|
|
else:
|
|
|
|
item['disabled'] = False
|
|
|
|
if 'enabled' in item:
|
|
|
|
if item['enabled'] in BOOLEANS_TRUE:
|
|
|
|
item['enabled'] = True
|
|
|
|
else:
|
|
|
|
item['enabled'] = False
|
|
|
|
if 'fullPath' in item:
|
|
|
|
item['full_path'] = item.pop('fullPath')
|
|
|
|
if 'memberOrder' in item:
|
|
|
|
item['member_order'] = int(item.pop('memberOrder'))
|
|
|
|
# Cast some attributes to integer
|
|
|
|
for x in ['order', 'preference', 'ratio', 'service']:
|
|
|
|
if x in item:
|
|
|
|
item[x] = int(item[x])
|
|
|
|
result.append(item)
|
|
|
|
return result
|
|
|
|
|
|
|
|
@property
|
|
|
|
def qos_hit_ratio(self):
|
|
|
|
if self._values['qos_hit_ratio'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['qos_hit_ratio'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def qos_hops(self):
|
|
|
|
if self._values['qos_hops'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['qos_hops'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def qos_kilobytes_second(self):
|
|
|
|
if self._values['qos_kilobytes_second'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['qos_kilobytes_second'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def qos_lcs(self):
|
|
|
|
if self._values['qos_lcs'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['qos_lcs'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def qos_packet_rate(self):
|
|
|
|
if self._values['qos_packet_rate'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['qos_packet_rate'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def qos_rtt(self):
|
|
|
|
if self._values['qos_rtt'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['qos_rtt'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def qos_topology(self):
|
|
|
|
if self._values['qos_topology'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['qos_topology'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def qos_vs_capacity(self):
|
|
|
|
if self._values['qos_vs_capacity'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['qos_vs_capacity'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def qos_vs_score(self):
|
|
|
|
if self._values['qos_vs_score'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['qos_vs_score'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def availability_state(self):
|
|
|
|
if self._values['stats'] is None:
|
|
|
|
return None
|
|
|
|
try:
|
|
|
|
result = self._values['stats']['status_availabilityState']
|
|
|
|
return result['description']
|
|
|
|
except AttributeError:
|
|
|
|
return None
|
|
|
|
|
|
|
|
@property
|
|
|
|
def enabled_state(self):
|
|
|
|
if self._values['stats'] is None:
|
|
|
|
return None
|
|
|
|
try:
|
|
|
|
result = self._values['stats']['status_enabledState']
|
|
|
|
return result['description']
|
|
|
|
except AttributeError:
|
|
|
|
return None
|
|
|
|
|
|
|
|
@property
|
|
|
|
def availability_status(self):
|
|
|
|
# This fact is a combination of the availability_state and enabled_state
|
|
|
|
#
|
|
|
|
# The purpose of the fact is to give a higher-level view of the availability
|
|
|
|
# of the pool, that can be used in playbooks. If you need further detail,
|
|
|
|
# consider using the following facts together.
|
|
|
|
#
|
|
|
|
# - availability_state
|
|
|
|
# - enabled_state
|
|
|
|
#
|
|
|
|
if self.enabled_state == 'enabled':
|
|
|
|
if self.availability_state == 'offline':
|
|
|
|
return 'red'
|
|
|
|
elif self.availability_state == 'available':
|
|
|
|
return 'green'
|
|
|
|
elif self.availability_state == 'unknown':
|
|
|
|
return 'blue'
|
|
|
|
else:
|
|
|
|
return 'none'
|
|
|
|
else:
|
|
|
|
# disabled
|
|
|
|
return 'black'
|
|
|
|
|
|
|
|
|
|
|
|
class WideIpParameters(BaseParameters):
|
|
|
|
api_map = {
|
|
|
|
'fullPath': 'full_path',
|
|
|
|
'failureRcode': 'failure_return_code',
|
|
|
|
'failureRcodeResponse': 'failure_return_code_response',
|
|
|
|
'failureRcodeTtl': 'failure_return_code_ttl',
|
|
|
|
'lastResortPool': 'last_resort_pool',
|
|
|
|
'minimalResponse': 'minimal_response',
|
|
|
|
'persistCidrIpv4': 'persist_cidr_ipv4',
|
|
|
|
'persistCidrIpv6': 'persist_cidr_ipv6',
|
|
|
|
'poolLbMode': 'pool_lb_mode',
|
|
|
|
'ttlPersistence': 'ttl_persistence'
|
|
|
|
}
|
|
|
|
|
|
|
|
returnables = [
|
|
|
|
'full_path', 'description', 'enabled', 'disabled', 'failure_return_code',
|
|
|
|
'failure_return_code_response', 'failure_return_code_ttl', 'last_resort_pool',
|
|
|
|
'minimal_response', 'persist_cidr_ipv4', 'persist_cidr_ipv6', 'pool_lb_mode',
|
|
|
|
'ttl_persistence', 'pools'
|
|
|
|
]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def pools(self):
|
|
|
|
result = []
|
|
|
|
if self._values['pools'] is None:
|
|
|
|
return []
|
|
|
|
for pool in self._values['pools']:
|
|
|
|
del pool['nameReference']
|
|
|
|
for x in ['order', 'ratio']:
|
|
|
|
if x in pool:
|
|
|
|
pool[x] = int(pool[x])
|
|
|
|
result.append(pool)
|
|
|
|
return result
|
|
|
|
|
|
|
|
@property
|
|
|
|
def failure_return_code_ttl(self):
|
|
|
|
if self._values['failure_return_code_ttl'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['failure_return_code_ttl'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def persist_cidr_ipv4(self):
|
|
|
|
if self._values['persist_cidr_ipv4'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['persist_cidr_ipv4'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def persist_cidr_ipv6(self):
|
|
|
|
if self._values['persist_cidr_ipv6'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['persist_cidr_ipv6'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def ttl_persistence(self):
|
|
|
|
if self._values['ttl_persistence'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['ttl_persistence'])
|
|
|
|
|
|
|
|
|
|
|
|
class ServerParameters(BaseParameters):
|
|
|
|
api_map = {
|
|
|
|
'fullPath': 'full_path',
|
|
|
|
'exposeRouteDomains': 'expose_route_domains',
|
|
|
|
'iqAllowPath': 'iq_allow_path',
|
|
|
|
'iqAllowServiceCheck': 'iq_allow_service_check',
|
|
|
|
'iqAllowSnmp': 'iq_allow_snmp',
|
|
|
|
'limitCpuUsage': 'limit_cpu_usage',
|
|
|
|
'limitCpuUsageStatus': 'limit_cpu_usage_status',
|
|
|
|
'limitMaxBps': 'limit_max_bps',
|
|
|
|
'limitMaxBpsStatus': 'limit_max_bps_status',
|
|
|
|
'limitMaxConnections': 'limit_max_connections',
|
|
|
|
'limitMaxConnectionsStatus': 'limit_max_connections_status',
|
|
|
|
'limitMaxPps': 'limit_max_pps',
|
|
|
|
'limitMaxPpsStatus': 'limit_max_pps_status',
|
|
|
|
'limitMemAvail': 'limit_mem_available',
|
|
|
|
'limitMemAvailStatus': 'limit_mem_available_status',
|
|
|
|
'linkDiscovery': 'link_discovery',
|
|
|
|
'proberFallback': 'prober_fallback',
|
|
|
|
'proberPreference': 'prober_preference',
|
|
|
|
'virtualServerDiscovery': 'virtual_server_discovery',
|
|
|
|
'devicesReference': 'devices',
|
|
|
|
'virtualServersReference': 'virtual_servers'
|
|
|
|
}
|
|
|
|
|
|
|
|
returnables = [
|
|
|
|
'datacenter', 'enabled', 'disabled', 'expose_route_domains', 'iq_allow_path',
|
|
|
|
'full_path', 'iq_allow_service_check', 'iq_allow_snmp', 'limit_cpu_usage',
|
|
|
|
'limit_cpu_usage_status', 'limit_max_bps', 'limit_max_bps_status',
|
|
|
|
'limit_max_connections', 'limit_max_connections_status', 'limit_max_pps',
|
|
|
|
'limit_max_pps_status', 'limit_mem_available', 'limit_mem_available_status',
|
|
|
|
'link_discovery', 'monitor', 'product', 'prober_fallback', 'prober_preference',
|
|
|
|
'virtual_server_discovery', 'addresses', 'devices', 'virtual_servers'
|
|
|
|
]
|
|
|
|
|
|
|
|
@property
|
|
|
|
def product(self):
|
|
|
|
if self._values['product'] is None:
|
|
|
|
return None
|
|
|
|
if self._values['product'] in ['single-bigip', 'redundant-bigip']:
|
|
|
|
return 'bigip'
|
|
|
|
return self._values['product']
|
|
|
|
|
|
|
|
@property
|
|
|
|
def devices(self):
|
|
|
|
result = []
|
|
|
|
if self._values['devices'] is None or 'items' not in self._values['devices']:
|
|
|
|
return result
|
|
|
|
for item in self._values['devices']['items']:
|
|
|
|
self._remove_internal_keywords(item)
|
|
|
|
if 'fullPath' in item:
|
|
|
|
item['full_path'] = item.pop('fullPath')
|
|
|
|
result.append(item)
|
|
|
|
return result
|
|
|
|
|
|
|
|
@property
|
|
|
|
def virtual_servers(self):
|
|
|
|
result = []
|
|
|
|
if self._values['virtual_servers'] is None or 'items' not in self._values['virtual_servers']:
|
|
|
|
return result
|
|
|
|
for item in self._values['virtual_servers']['items']:
|
|
|
|
self._remove_internal_keywords(item)
|
|
|
|
if 'disabled' in item:
|
|
|
|
if item['disabled'] in BOOLEANS_TRUE:
|
|
|
|
item['disabled'] = True
|
|
|
|
else:
|
|
|
|
item['disabled'] = False
|
|
|
|
if 'enabled' in item:
|
|
|
|
if item['enabled'] in BOOLEANS_TRUE:
|
|
|
|
item['enabled'] = True
|
|
|
|
else:
|
|
|
|
item['enabled'] = False
|
|
|
|
if 'fullPath' in item:
|
|
|
|
item['full_path'] = item.pop('fullPath')
|
|
|
|
if 'limitMaxBps' in item:
|
|
|
|
item['limit_max_bps'] = int(item.pop('limitMaxBps'))
|
|
|
|
if 'limitMaxBpsStatus' in item:
|
|
|
|
item['limit_max_bps_status'] = item.pop('limitMaxBpsStatus')
|
|
|
|
if 'limitMaxConnections' in item:
|
|
|
|
item['limit_max_connections'] = int(item.pop('limitMaxConnections'))
|
|
|
|
if 'limitMaxConnectionsStatus' in item:
|
|
|
|
item['limit_max_connections_status'] = item.pop('limitMaxConnectionsStatus')
|
|
|
|
if 'limitMaxPps' in item:
|
|
|
|
item['limit_max_pps'] = int(item.pop('limitMaxPps'))
|
|
|
|
if 'limitMaxPpsStatus' in item:
|
|
|
|
item['limit_max_pps_status'] = item.pop('limitMaxPpsStatus')
|
|
|
|
if 'translationAddress' in item:
|
|
|
|
item['translation_address'] = item.pop('translationAddress')
|
|
|
|
if 'translationPort' in item:
|
|
|
|
item['translation_port'] = int(item.pop('translationPort'))
|
|
|
|
result.append(item)
|
|
|
|
return result
|
|
|
|
|
|
|
|
@property
|
|
|
|
def limit_cpu_usage(self):
|
|
|
|
if self._values['limit_cpu_usage'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['limit_cpu_usage'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def limit_max_bps(self):
|
|
|
|
if self._values['limit_max_bps'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['limit_max_bps'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def limit_max_connections(self):
|
|
|
|
if self._values['limit_max_connections'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['limit_max_connections'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def limit_max_pps(self):
|
|
|
|
if self._values['limit_max_pps'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['limit_max_pps'])
|
|
|
|
|
|
|
|
@property
|
|
|
|
def limit_mem_available(self):
|
|
|
|
if self._values['limit_mem_available'] is None:
|
|
|
|
return None
|
|
|
|
return int(self._values['limit_mem_available'])
|
|
|
|
|
|
|
|
|
|
|
|
class PoolFactManager(BaseManager):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.module = kwargs.get('module', None)
|
|
|
|
self.client = kwargs.get('client', None)
|
|
|
|
super(PoolFactManager, self).__init__(**kwargs)
|
|
|
|
self.kwargs = kwargs
|
|
|
|
|
|
|
|
def exec_module(self):
|
|
|
|
if self.version_is_less_than_12():
|
|
|
|
manager = self.get_manager('untyped')
|
|
|
|
else:
|
|
|
|
manager = self.get_manager('typed')
|
|
|
|
facts = manager.exec_module()
|
|
|
|
result = dict(pool=facts)
|
|
|
|
return result
|
|
|
|
|
|
|
|
def get_manager(self, type):
|
|
|
|
if type == 'typed':
|
|
|
|
return TypedPoolFactManager(**self.kwargs)
|
|
|
|
elif type == 'untyped':
|
|
|
|
return UntypedPoolFactManager(**self.kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
class TypedPoolFactManager(TypedManager):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.module = kwargs.get('module', None)
|
|
|
|
self.client = kwargs.get('client', None)
|
|
|
|
super(TypedPoolFactManager, self).__init__(**kwargs)
|
|
|
|
self.want = PoolParameters(params=self.module.params)
|
|
|
|
|
|
|
|
def read_facts(self, collection):
|
|
|
|
results = []
|
|
|
|
collection = self.read_collection_from_device(collection)
|
|
|
|
for resource in collection:
|
|
|
|
attrs = resource.attrs
|
|
|
|
attrs['stats'] = self.read_stats_from_device(resource)
|
|
|
|
params = PoolParameters(params=attrs)
|
|
|
|
results.append(params)
|
|
|
|
return results
|
|
|
|
|
|
|
|
def read_collection_from_device(self, collection_name):
|
|
|
|
pools = self.client.api.tm.gtm.pools
|
|
|
|
collection = getattr(pools, collection_name)
|
|
|
|
result = collection.get_collection(
|
|
|
|
requests_params=dict(
|
|
|
|
params='expandSubcollections=true'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
class UntypedPoolFactManager(UntypedManager):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.client = kwargs.get('client', None)
|
|
|
|
self.module = kwargs.get('module', None)
|
|
|
|
super(UntypedPoolFactManager, self).__init__(**kwargs)
|
|
|
|
self.want = PoolParameters(params=self.module.params)
|
|
|
|
|
|
|
|
def read_facts(self):
|
|
|
|
results = []
|
|
|
|
collection = self.read_collection_from_device()
|
|
|
|
for resource in collection:
|
|
|
|
attrs = resource.attrs
|
|
|
|
attrs['stats'] = self.read_stats_from_device(resource)
|
|
|
|
params = PoolParameters(params=attrs)
|
|
|
|
results.append(params)
|
|
|
|
return results
|
|
|
|
|
|
|
|
def read_collection_from_device(self):
|
|
|
|
result = self.client.api.tm.gtm.pools.get_collection(
|
|
|
|
requests_params=dict(
|
|
|
|
params='expandSubcollections=true'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
class WideIpFactManager(BaseManager):
|
|
|
|
def exec_module(self):
|
|
|
|
if self.version_is_less_than_12():
|
|
|
|
manager = self.get_manager('untyped')
|
|
|
|
else:
|
|
|
|
manager = self.get_manager('typed')
|
|
|
|
facts = manager.exec_module()
|
|
|
|
result = dict(wide_ip=facts)
|
|
|
|
return result
|
|
|
|
|
|
|
|
def get_manager(self, type):
|
|
|
|
if type == 'typed':
|
|
|
|
return TypedWideIpFactManager(**self.kwargs)
|
|
|
|
elif type == 'untyped':
|
|
|
|
return UntypedWideIpFactManager(**self.kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
class TypedWideIpFactManager(TypedManager):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.client = kwargs.get('client', None)
|
|
|
|
self.module = kwargs.get('module', None)
|
|
|
|
super(TypedWideIpFactManager, self).__init__(**kwargs)
|
|
|
|
self.want = WideIpParameters(params=self.module.params)
|
|
|
|
|
|
|
|
def read_facts(self, collection):
|
|
|
|
results = []
|
|
|
|
collection = self.read_collection_from_device(collection)
|
|
|
|
for resource in collection:
|
|
|
|
attrs = resource.attrs
|
|
|
|
params = WideIpParameters(params=attrs)
|
|
|
|
results.append(params)
|
|
|
|
return results
|
|
|
|
|
|
|
|
def read_collection_from_device(self, collection_name):
|
|
|
|
wideips = self.client.api.tm.gtm.wideips
|
|
|
|
collection = getattr(wideips, collection_name)
|
|
|
|
result = collection.get_collection(
|
|
|
|
requests_params=dict(
|
|
|
|
params='expandSubcollections=true'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
class UntypedWideIpFactManager(UntypedManager):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.client = kwargs.get('client', None)
|
|
|
|
self.module = kwargs.get('module', None)
|
|
|
|
super(UntypedWideIpFactManager, self).__init__(**kwargs)
|
|
|
|
self.want = WideIpParameters(params=self.module.params)
|
|
|
|
|
|
|
|
def read_facts(self):
|
|
|
|
results = []
|
|
|
|
collection = self.read_collection_from_device()
|
|
|
|
for resource in collection:
|
|
|
|
attrs = resource.attrs
|
|
|
|
params = WideIpParameters(params=attrs)
|
|
|
|
results.append(params)
|
|
|
|
return results
|
|
|
|
|
|
|
|
def read_collection_from_device(self):
|
|
|
|
result = self.client.api.tm.gtm.wideips.get_collection(
|
|
|
|
requests_params=dict(
|
|
|
|
params='expandSubcollections=true'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
class ServerFactManager(UntypedManager):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.client = kwargs.get('client', None)
|
|
|
|
self.module = kwargs.get('module', None)
|
|
|
|
super(ServerFactManager, self).__init__(**kwargs)
|
|
|
|
self.want = ServerParameters(params=self.module.params)
|
|
|
|
|
|
|
|
def exec_module(self):
|
|
|
|
facts = super(ServerFactManager, self).exec_module()
|
|
|
|
result = dict(server=facts)
|
|
|
|
return result
|
|
|
|
|
|
|
|
def read_facts(self):
|
|
|
|
results = []
|
|
|
|
collection = self.read_collection_from_device()
|
|
|
|
for resource in collection:
|
|
|
|
attrs = resource.attrs
|
|
|
|
params = ServerParameters(params=attrs)
|
|
|
|
results.append(params)
|
|
|
|
return results
|
|
|
|
|
|
|
|
def read_collection_from_device(self):
|
|
|
|
result = self.client.api.tm.gtm.servers.get_collection(
|
|
|
|
requests_params=dict(
|
|
|
|
params='expandSubcollections=true'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
class ModuleManager(object):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.module = kwargs.get('module', None)
|
|
|
|
self.client = kwargs.get('client', None)
|
|
|
|
self.kwargs = kwargs
|
|
|
|
self.want = Parameters(params=self.module.params)
|
|
|
|
|
|
|
|
def exec_module(self):
|
|
|
|
if not self.gtm_provisioned():
|
|
|
|
raise F5ModuleError(
|
|
|
|
"GTM must be provisioned to use this module."
|
|
|
|
)
|
|
|
|
|
|
|
|
if 'all' in self.want.include:
|
|
|
|
names = ['pool', 'wide_ip', 'server']
|
|
|
|
else:
|
|
|
|
names = self.want.include
|
|
|
|
managers = [self.get_manager(name) for name in names]
|
|
|
|
result = self.execute_managers(managers)
|
|
|
|
if result:
|
|
|
|
result['changed'] = True
|
|
|
|
else:
|
|
|
|
result['changed'] = False
|
|
|
|
self._announce_deprecations()
|
|
|
|
return result
|
|
|
|
|
|
|
|
def _announce_deprecations(self):
|
|
|
|
warnings = []
|
|
|
|
if self.want:
|
|
|
|
warnings += self.want._values.get('__warnings', [])
|
|
|
|
for warning in warnings:
|
|
|
|
self.module.deprecate(
|
|
|
|
msg=warning['msg'],
|
|
|
|
version=warning['version']
|
|
|
|
)
|
|
|
|
|
|
|
|
def execute_managers(self, managers):
|
|
|
|
results = dict()
|
|
|
|
for manager in managers:
|
|
|
|
result = manager.exec_module()
|
|
|
|
results.update(result)
|
|
|
|
return results
|
|
|
|
|
|
|
|
def get_manager(self, which):
|
|
|
|
if 'pool' == which:
|
|
|
|
return PoolFactManager(**self.kwargs)
|
|
|
|
if 'wide_ip' == which:
|
|
|
|
return WideIpFactManager(**self.kwargs)
|
|
|
|
if 'server' == which:
|
|
|
|
return ServerFactManager(**self.kwargs)
|
|
|
|
|
|
|
|
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 ArgumentSpec(object):
|
|
|
|
def __init__(self):
|
|
|
|
self.supports_check_mode = False
|
|
|
|
argument_spec = dict(
|
|
|
|
include=dict(
|
|
|
|
type='list',
|
|
|
|
choices=[
|
|
|
|
'pool',
|
|
|
|
'wide_ip',
|
|
|
|
'server',
|
|
|
|
],
|
|
|
|
required=True
|
|
|
|
),
|
|
|
|
filter=dict()
|
|
|
|
)
|
|
|
|
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")
|
|
|
|
|
|
|
|
client = F5Client(**module.params)
|
|
|
|
|
|
|
|
try:
|
|
|
|
mm = ModuleManager(module=module, client=client)
|
|
|
|
results = mm.exec_module()
|
|
|
|
module.exit_json(**results)
|
|
|
|
except F5ModuleError as ex:
|
|
|
|
module.fail_json(msg=str(ex))
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|