Various f5 fixes (#44858)

* Remove sdk from modules
* Correct IP address bugs in data_group
* Correct compare_dictionary bug in several modules
pull/38159/merge
Tim Rupp 6 years ago committed by GitHub
parent 773c0982b0
commit 5bdcaff921
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -335,28 +335,34 @@ def transform_name(partition='', name='', sub_path=''):
return result return result
def dict2tuple(items): def compare_complex_list(want, have):
"""Convert a dictionary to a list of tuples """Performs a complex list comparison
This method is used in cases where dictionaries need to be compared. Due A complex list is a list of dictionaries
to dictionaries inherently having no order, it is easier to compare list
of tuples because these lists can be converted to sets.
This conversion only supports dicts of simple values. Do not give it dicts
that contain sub-dicts. This will not give you the result you want when using
the returned tuple for comparison.
Args: Args:
items (dict): The dictionary of items that should be converted want (list): List of dictionaries to compare with second parameter.
have (list): List of dictionaries compare with first parameter.
Returns: Returns:
list: Returns a list of tuples upon success. Otherwise, an empty list. bool:
""" """
result = [] if want == [] and have is None:
for x in items: return None
if want is None:
return None
w = []
h = []
for x in want:
tmp = [(str(k), str(v)) for k, v in iteritems(x)] tmp = [(str(k), str(v)) for k, v in iteritems(x)]
result += tmp w += tmp
return result for x in have:
tmp = [(str(k), str(v)) for k, v in iteritems(x)]
h += tmp
if set(w) == set(h):
return None
else:
return want
def compare_dictionary(want, have): def compare_dictionary(want, have):
@ -369,12 +375,12 @@ def compare_dictionary(want, have):
Returns: Returns:
bool: bool:
""" """
if want == [] and have is None: if want == {} and have is None:
return None return None
if want is None: if want is None:
return None return None
w = dict2tuple(want) w = [(str(k), str(v)) for k, v in iteritems(want)]
h = dict2tuple(have) h = [(str(k), str(v)) for k, v in iteritems(have)]
if set(w) == set(h): if set(w) == set(h):
return None return None
else: else:

@ -265,7 +265,7 @@ try:
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 compare_dictionary from library.module_utils.network.f5.common import compare_complex_list
from library.module_utils.network.f5.common import f5_argument_spec from library.module_utils.network.f5.common import f5_argument_spec
from library.module_utils.network.f5.ipaddress import is_valid_ip from library.module_utils.network.f5.ipaddress import is_valid_ip
from library.module_utils.network.f5.ipaddress import is_valid_ip_network from library.module_utils.network.f5.ipaddress import is_valid_ip_network
@ -283,7 +283,7 @@ except ImportError:
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 compare_dictionary from ansible.module_utils.network.f5.common import compare_complex_list
from ansible.module_utils.network.f5.common import f5_argument_spec from ansible.module_utils.network.f5.common import f5_argument_spec
from ansible.module_utils.network.f5.ipaddress import is_valid_ip from ansible.module_utils.network.f5.ipaddress import is_valid_ip
from ansible.module_utils.network.f5.ipaddress import is_valid_ip_network from ansible.module_utils.network.f5.ipaddress import is_valid_ip_network
@ -351,32 +351,23 @@ class RecordsEncoder(object):
return self.encode_string_from_dict(record) return self.encode_string_from_dict(record)
def encode_address_from_dict(self, record): def encode_address_from_dict(self, record):
if is_valid_ip_network(record['key']): if is_valid_ip_interface(record['key']):
key = ip_network(u"{0}".format(str(record['key'])))
elif is_valid_ip(record['key']):
key = ip_address(u"{0}".format(str(record['key'])))
elif is_valid_ip_interface(record['key']):
key = ip_interface(u"{0}".format(str(record['key']))) key = ip_interface(u"{0}".format(str(record['key'])))
else: else:
raise F5ModuleError( raise F5ModuleError(
"When specifying an 'address' type, the value to the left of the separator must be an IP." "When specifying an 'address' type, the value to the left of the separator must be an IP."
) )
if key and 'value' in record: if key and 'value' in record:
try: if key.network.prefixlen in [32, 128]:
# Only ip_address's have max_prefixlen return self.encode_host(str(key.ip), record['value'])
if key.max_prefixlen in [32, 128]:
return self.encode_host(str(key), record['value'])
except ValueError:
return self.encode_network( return self.encode_network(
str(key.network_address), key.prefixlen, record['value']) str(key.network.network_address), key.network.prefixlen, record['value']
)
elif key: elif key:
try: if key.network.prefixlen in [32, 128]:
# Only ip_address's have max_prefixlen return self.encode_host(str(key.ip), str(key.ip))
if key.max_prefixlen in [32, 128]:
return self.encode_host(str(key), str(key))
except ValueError:
return self.encode_network( return self.encode_network(
str(key.network_address), key.prefixlen, str(key.network_address) str(key.network.network_address), key.network.prefixlen, str(key.network.network_address)
) )
def encode_integer_from_dict(self, record): def encode_integer_from_dict(self, record):
@ -419,35 +410,25 @@ class RecordsEncoder(object):
# 192.168.0.0/16 := "Network3", # 192.168.0.0/16 := "Network3",
# 2402:9400:1000:0::/64 := "Network4", # 2402:9400:1000:0::/64 := "Network4",
parts = record.split(self._separator) parts = record.split(self._separator)
if is_valid_ip_network(parts[0]): if parts[0] == '':
key = ip_network(u"{0}".format(str(parts[0]))) return
elif is_valid_ip(parts[0]): if not is_valid_ip_interface(parts[0]):
key = ip_address(u"{0}".format(str(parts[0])))
elif is_valid_ip_interface(parts[0]):
key = ip_interface(u"{0}".format(str(parts[0])))
elif parts[0] == '':
pass
else:
raise F5ModuleError( raise F5ModuleError(
"When specifying an 'address' type, the value to the left of the separator must be an IP." "When specifying an 'address' type, the value to the left of the separator must be an IP."
) )
key = ip_interface(u"{0}".format(str(parts[0])))
if len(parts) == 2: if len(parts) == 2:
try: if key.network.prefixlen in [32, 128]:
# Only ip_address's have max_prefixlen return self.encode_host(str(key.ip), parts[1])
if key.max_prefixlen in [32, 128]:
return self.encode_host(str(key), parts[1])
except ValueError:
return self.encode_network( return self.encode_network(
str(key.network_address), key.prefixlen, parts[1]) str(key.network.network_address), key.network.prefixlen, parts[1]
)
elif len(parts) == 1 and parts[0] != '': elif len(parts) == 1 and parts[0] != '':
try: if key.network.prefixlen in [32, 128]:
# Only ip_address's have max_prefixlen return self.encode_host(str(key.ip), str(key.ip))
if key.max_prefixlen in [32, 128]:
return self.encode_host(str(key), str(key))
except ValueError:
return self.encode_network( return self.encode_network(
str(key.network_address), key.prefixlen, str(key.network_address) str(key.network.network_address), key.network.prefixlen, str(key.network.network_address)
) )
def encode_host(self, key, value): def encode_host(self, key, value):
@ -706,7 +687,7 @@ class Difference(object):
return None return None
if self.have.records is None: if self.have.records is None:
return self.want.records return self.want.records
result = compare_dictionary(self.want.records, self.have.records) result = compare_complex_list(self.want.records, self.have.records)
return result return result
@property @property

@ -415,7 +415,7 @@ class Difference(object):
) )
result = dict() result = dict()
different = compare_dictionary([self.want.variables], [self.have.variables]) different = compare_dictionary(self.want.variables, self.have.variables)
if not different: if not different:
return None return None

@ -244,13 +244,14 @@ from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.basic import env_fallback from ansible.module_utils.basic import env_fallback
try: try:
from library.module_utils.compat.ipaddress import ip_address
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 fq_name from library.module_utils.network.f5.common import fq_name
from library.module_utils.network.f5.common import compare_dictionary from library.module_utils.network.f5.common import compare_complex_list
from library.module_utils.network.f5.common import f5_argument_spec from library.module_utils.network.f5.common import f5_argument_spec
from library.module_utils.network.f5.ipaddress import is_valid_ip from library.module_utils.network.f5.ipaddress import is_valid_ip
from library.module_utils.network.f5.ipaddress import validate_ip_v6_address from library.module_utils.network.f5.ipaddress import validate_ip_v6_address
@ -260,13 +261,14 @@ try:
except ImportError: except ImportError:
HAS_F5SDK = False HAS_F5SDK = False
except ImportError: except ImportError:
from ansible.module_utils.compat.ipaddress import ip_address
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 fq_name from ansible.module_utils.network.f5.common import fq_name
from ansible.module_utils.network.f5.common import compare_dictionary from ansible.module_utils.network.f5.common import compare_complex_list
from ansible.module_utils.network.f5.common import f5_argument_spec from ansible.module_utils.network.f5.common import f5_argument_spec
from ansible.module_utils.network.f5.ipaddress import is_valid_ip from ansible.module_utils.network.f5.ipaddress import is_valid_ip
from ansible.module_utils.network.f5.ipaddress import validate_ip_v6_address from ansible.module_utils.network.f5.ipaddress import validate_ip_v6_address
@ -534,7 +536,8 @@ class ModuleParameters(Parameters):
if self._values['address'] is None: if self._values['address'] is None:
return None return None
if is_valid_ip(self._values['address']): if is_valid_ip(self._values['address']):
return self._values['address'] ip = str(ip_address(u'{0}'.format(self._values['address'])))
return ip
raise F5ModuleError( raise F5ModuleError(
"Specified 'address' is not an IP address." "Specified 'address' is not an IP address."
) )
@ -758,7 +761,8 @@ class Difference(object):
return None return None
if self.want.virtual_server_dependencies is None: if self.want.virtual_server_dependencies is None:
return None return None
return compare_dictionary(self.want.virtual_server_dependencies, self.have.virtual_server_dependencies) result = compare_complex_list(self.want.virtual_server_dependencies, self.have.virtual_server_dependencies)
return result
@property @property
def enabled(self): def enabled(self):

@ -7,7 +7,6 @@
from __future__ import absolute_import, division, print_function from __future__ import absolute_import, division, print_function
__metaclass__ = type __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['preview'],
'supported_by': 'community'} 'supported_by': 'community'}
@ -159,7 +158,6 @@ timeout:
sample: 10 sample: 10
''' '''
import os
import re import re
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
@ -167,33 +165,29 @@ from ansible.module_utils.basic import env_fallback
from ansible.module_utils.six import iteritems from ansible.module_utils.six import iteritems
try: try:
from library.module_utils.network.f5.bigip import HAS_F5SDK from library.module_utils.network.f5.bigip import F5RestClient
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 fq_name from library.module_utils.network.f5.common import fq_name
from library.module_utils.network.f5.common import f5_argument_spec from library.module_utils.network.f5.common import f5_argument_spec
from library.module_utils.network.f5.common import transform_name
from library.module_utils.network.f5.common import exit_json
from library.module_utils.network.f5.common import fail_json
from library.module_utils.network.f5.common import compare_dictionary from library.module_utils.network.f5.common import compare_dictionary
from library.module_utils.network.f5.ipaddress import is_valid_ip from library.module_utils.network.f5.ipaddress import is_valid_ip
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
except ImportError: except ImportError:
from ansible.module_utils.network.f5.bigip import HAS_F5SDK from ansible.module_utils.network.f5.bigip import F5RestClient
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 fq_name from ansible.module_utils.network.f5.common import fq_name
from ansible.module_utils.network.f5.common import f5_argument_spec from ansible.module_utils.network.f5.common import f5_argument_spec
from ansible.module_utils.network.f5.common import transform_name
from ansible.module_utils.network.f5.common import compare_dictionary from ansible.module_utils.network.f5.common import compare_dictionary
from ansible.module_utils.network.f5.common import exit_json
from ansible.module_utils.network.f5.common import fail_json
from ansible.module_utils.network.f5.ipaddress import is_valid_ip from ansible.module_utils.network.f5.ipaddress import is_valid_ip
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
@ -201,33 +195,24 @@ class Parameters(AnsibleF5Parameters):
'defaultsFrom': 'parent', 'defaultsFrom': 'parent',
'apiRawValues': 'variables', 'apiRawValues': 'variables',
'run': 'external_program', 'run': 'external_program',
'args': 'arguments' 'args': 'arguments',
} }
api_attributes = [ api_attributes = [
'defaultsFrom', 'interval', 'timeout', 'destination', 'run', 'args', 'description' 'defaultsFrom', 'interval', 'timeout', 'destination', 'run', 'args',
'description',
] ]
returnables = [ returnables = [
'parent', 'ip', 'port', 'interval', 'timeout', 'variables', 'external_program', 'parent', 'ip', 'port', 'interval', 'timeout', 'variables', 'external_program',
'arguments', 'description' 'arguments', 'description',
] ]
updatables = [ updatables = [
'destination', 'interval', 'timeout', 'variables', 'external_program', 'destination', 'interval', 'timeout', 'variables', 'external_program',
'arguments', 'description' 'arguments', 'description',
] ]
def to_return(self):
result = {}
try:
for returnable in self.returnables:
result[returnable] = getattr(self, returnable)
result = self._filter_params(result)
except Exception:
pass
return result
@property @property
def destination(self): def destination(self):
if self.ip is None and self.port is None: if self.ip is None and self.port is None:
@ -285,11 +270,7 @@ class Parameters(AnsibleF5Parameters):
def parent(self): def parent(self):
if self._values['parent'] is None: if self._values['parent'] is None:
return None return None
if self._values['parent'].startswith('/'): result = fq_name(self.partition, self._values['parent'])
parent = os.path.basename(self._values['parent'])
result = '/{0}/{1}'.format(self.partition, parent)
else:
result = '/{0}/{1}'.format(self.partition, self._values['parent'])
return result return result
@property @property
@ -426,8 +407,7 @@ class Difference(object):
variables=self.want.variables variables=self.want.variables
) )
result = dict() result = dict()
different = compare_dictionary(self.want.variables, self.have.variables)
different = compare_dictionary([self.want.variables], [self.have.variables])
if not different: if not different:
return None return None
@ -491,13 +471,10 @@ class ModuleManager(object):
result = dict() result = dict()
state = self.want.state state = self.want.state
try:
if state == "present": if state == "present":
changed = self.present() changed = self.present()
elif state == "absent": elif state == "absent":
changed = self.absent() changed = self.absent()
except iControlUnexpectedHTTPError as e:
raise F5ModuleError(str(e))
reportable = ReportableChanges(params=self.changes.to_return()) reportable = ReportableChanges(params=self.changes.to_return())
changes = reportable.to_return() changes = reportable.to_return()
@ -520,19 +497,19 @@ class ModuleManager(object):
else: else:
return self.create() return self.create()
def create(self): def exists(self):
self._set_changed_options() uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/external/{2}".format(
if self.want.timeout is None: self.client.provider['server'],
self.want.update({'timeout': 16}) self.client.provider['server_port'],
if self.want.interval is None: transform_name(self.want.partition, self.want.name)
self.want.update({'interval': 5}) )
if self.want.ip is None: resp = self.client.api.get(uri)
self.want.update({'ip': '*'}) try:
if self.want.port is None: response = resp.json()
self.want.update({'port': '*'}) except ValueError:
if self.module.check_mode: return False
return True if resp.status == 404 or 'code' in response and response['code'] == 404:
self.create_on_device() return False
return True return True
def update(self): def update(self):
@ -544,70 +521,132 @@ class ModuleManager(object):
self.update_on_device() self.update_on_device()
return True return True
def absent(self):
if self.exists():
return self.remove()
return False
def remove(self): def remove(self):
if self.module.check_mode: if self.module.check_mode:
return True return True
self.remove_from_device() self.remove_from_device()
if self.exists(): if self.exists():
raise F5ModuleError("Failed to delete the monitor.") raise F5ModuleError("Failed to delete the resource.")
return True return True
def read_current_from_device(self): def create(self):
resource = self.client.api.tm.ltm.monitor.externals.external.load( self._set_changed_options()
name=self.want.name, self._set_default_creation_values()
partition=self.want.partition if self.module.check_mode:
) return True
result = resource.attrs self.create_on_device()
return ApiParameters(params=result) return True
def exists(self): def _set_default_creation_values(self):
result = self.client.api.tm.ltm.monitor.externals.external.exists( if self.want.timeout is None:
name=self.want.name, self.want.update({'timeout': 16})
partition=self.want.partition if self.want.interval is None:
) self.want.update({'interval': 5})
return result if self.want.ip is None:
self.want.update({'ip': '*'})
if self.want.port is None:
self.want.update({'port': '*'})
def update_on_device(self): def create_on_device(self):
params = self.changes.api_params() params = self.changes.api_params()
result = self.client.api.tm.ltm.monitor.externals.external.load( params['name'] = self.want.name
name=self.want.name, params['partition'] = self.want.partition
partition=self.want.partition uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/external/".format(
self.client.provider['server'],
self.client.provider['server_port']
) )
if params: resp = self.client.api.post(uri, json=params)
result.modify(**params) try:
if self.changes.variables: response = resp.json()
self.set_variable_on_device(self.changes.variables) except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] in [400, 403]:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
if self.want.variables:
self.set_variable_on_device(self.want.variables)
def set_variable_on_device(self, commands): def set_variable_on_device(self, commands):
command = ' '.join(['user-defined {0} \\\"{1}\\\"'.format(k, v) for k, v in iteritems(commands)]) command = ' '.join(['user-defined {0} \\\"{1}\\\"'.format(k, v) for k, v in iteritems(commands)])
command = 'tmsh modify ltm monitor external {0} {1}'.format(self.want.name, command) command = 'tmsh modify ltm monitor external {0} {1}'.format(self.want.name, command)
self.client.api.tm.util.bash.exec_cmd( uri = "https://{0}:{1}/mgmt/tm/util/bash".format(
'run', self.client.provider['server'],
self.client.provider['server_port'],
)
args = dict(
command='run',
utilCmdArgs='-c "{0}"'.format(command) utilCmdArgs='-c "{0}"'.format(command)
) )
resp = self.client.api.post(uri, json=args)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] == 400:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
def create_on_device(self): def update_on_device(self):
params = self.want.api_params() params = self.changes.api_params()
self.client.api.tm.ltm.monitor.externals.external.create( if params:
name=self.want.name, uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/external/{2}".format(
partition=self.want.partition, self.client.provider['server'],
**params self.client.provider['server_port'],
transform_name(self.want.partition, self.want.name)
) )
if self.want.variables: resp = self.client.api.patch(uri, json=params)
self.set_variable_on_device(self.want.variables) try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] == 400:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
if self.changes.variables:
self.set_variable_on_device(self.changes.variables)
def absent(self):
if self.exists():
return self.remove()
return False
def remove_from_device(self): def remove_from_device(self):
result = self.client.api.tm.ltm.monitor.externals.external.load( uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/external/{2}".format(
name=self.want.name, self.client.provider['server'],
partition=self.want.partition self.client.provider['server_port'],
transform_name(self.want.partition, self.want.name)
) )
if result: resp = self.client.api.delete(uri)
result.delete() if resp.status == 200:
return True
def read_current_from_device(self):
uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/external/{2}".format(
self.client.provider['server'],
self.client.provider['server_port'],
transform_name(self.want.partition, self.want.name)
)
resp = self.client.api.get(uri)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] == 400:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
return ApiParameters(params=response)
class ArgumentSpec(object): class ArgumentSpec(object):
@ -643,20 +682,19 @@ def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=spec.supports_check_mode supports_check_mode=spec.supports_check_mode,
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required") client = F5RestClient(**module.params)
try: try:
client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client) mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
cleanup_tokens(client) cleanup_tokens(client)
module.exit_json(**results) exit_json(module, results, client)
except F5ModuleError as ex: except F5ModuleError as ex:
cleanup_tokens(client) cleanup_tokens(client)
module.fail_json(msg=str(ex)) fail_json(module, ex, client)
if __name__ == '__main__': if __name__ == '__main__':

@ -478,12 +478,6 @@ class ModuleManager(object):
self.update_on_device() self.update_on_device()
return True return True
def should_update(self):
result = self._update_changed_options()
if result:
return True
return False
def remove(self): def remove(self):
if self.module.check_mode: if self.module.check_mode:
return True return True

@ -155,69 +155,57 @@ time_until_up:
sample: 2 sample: 2
''' '''
import os
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
try: try:
from library.module_utils.network.f5.bigip import HAS_F5SDK from library.module_utils.network.f5.bigip import F5RestClient
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 fq_name
from library.module_utils.network.f5.common import f5_argument_spec from library.module_utils.network.f5.common import f5_argument_spec
from library.module_utils.network.f5.common import transform_name
from library.module_utils.network.f5.common import exit_json
from library.module_utils.network.f5.common import fail_json
from library.module_utils.network.f5.ipaddress import is_valid_ip from library.module_utils.network.f5.ipaddress import is_valid_ip
try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
except ImportError: except ImportError:
from ansible.module_utils.network.f5.bigip import HAS_F5SDK from ansible.module_utils.network.f5.bigip import F5RestClient
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 fq_name
from ansible.module_utils.network.f5.common import f5_argument_spec from ansible.module_utils.network.f5.common import f5_argument_spec
from ansible.module_utils.network.f5.common import transform_name
from ansible.module_utils.network.f5.common import exit_json
from ansible.module_utils.network.f5.common import fail_json
from ansible.module_utils.network.f5.ipaddress import is_valid_ip from ansible.module_utils.network.f5.ipaddress import is_valid_ip
try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError:
HAS_F5SDK = False
class Parameters(AnsibleF5Parameters): class Parameters(AnsibleF5Parameters):
api_map = { api_map = {
'timeUntilUp': 'time_until_up', 'timeUntilUp': 'time_until_up',
'defaultsFrom': 'parent', 'defaultsFrom': 'parent',
'recv': 'receive' 'recv': 'receive',
} }
api_attributes = [ api_attributes = [
'timeUntilUp', 'defaultsFrom', 'interval', 'timeout', 'recv', 'send', 'timeUntilUp', 'defaultsFrom', 'interval', 'timeout', 'recv', 'send',
'destination', 'description' 'destination', 'description',
] ]
returnables = [ returnables = [
'parent', 'send', 'receive', 'ip', 'port', 'interval', 'timeout', 'parent', 'send', 'receive', 'ip', 'port', 'interval', 'timeout',
'time_until_up', 'description' 'time_until_up', 'description',
] ]
updatables = [ updatables = [
'destination', 'send', 'receive', 'interval', 'timeout', 'time_until_up', 'destination', 'send', 'receive', 'interval', 'timeout', 'time_until_up',
'description' 'description',
] ]
def to_return(self):
result = {}
try:
for returnable in self.returnables:
result[returnable] = getattr(self, returnable)
result = self._filter_params(result)
except Exception:
pass
return result
@property @property
def destination(self): def destination(self):
if self.ip is None and self.port is None: if self.ip is None and self.port is None:
@ -281,11 +269,7 @@ class Parameters(AnsibleF5Parameters):
def parent(self): def parent(self):
if self._values['parent'] is None: if self._values['parent'] is None:
return None return None
if self._values['parent'].startswith('/'): result = fq_name(self.partition, self._values['parent'])
parent = os.path.basename(self._values['parent'])
result = '/{0}/{1}'.format(self.partition, parent)
else:
result = '/{0}/{1}'.format(self.partition, self._values['parent'])
return result return result
@property @property
@ -293,7 +277,31 @@ class Parameters(AnsibleF5Parameters):
return 'udp' return 'udp'
class ApiParameters(Parameters):
pass
class ModuleParameters(Parameters):
pass
class Changes(Parameters): class Changes(Parameters):
def to_return(self):
result = {}
try:
for returnable in self.returnables:
result[returnable] = getattr(self, returnable)
result = self._filter_params(result)
except Exception:
pass
return result
class UsableChanges(Changes):
pass
class ReportableChanges(Changes):
pass pass
@ -368,9 +376,9 @@ class ModuleManager(object):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.module = kwargs.get('module', None) self.module = kwargs.get('module', None)
self.client = kwargs.get('client', None) self.client = kwargs.get('client', None)
self.have = None self.want = ModuleParameters(params=self.module.params)
self.want = Parameters(params=self.module.params) self.have = ApiParameters()
self.changes = Changes() self.changes = UsableChanges()
def _set_changed_options(self): def _set_changed_options(self):
changed = {} changed = {}
@ -378,7 +386,7 @@ class ModuleManager(object):
if getattr(self.want, key) is not None: if getattr(self.want, key) is not None:
changed[key] = getattr(self.want, key) changed[key] = getattr(self.want, key)
if changed: if changed:
self.changes = Changes(params=changed) self.changes = UsableChanges(params=changed)
def _update_changed_options(self): def _update_changed_options(self):
diff = Difference(self.want, self.have) diff = Difference(self.want, self.have)
@ -394,7 +402,7 @@ class ModuleManager(object):
else: else:
changed[k] = change changed[k] = change
if changed: if changed:
self.changes = Changes(params=changed) self.changes = UsableChanges(params=changed)
return True return True
return False return False
@ -409,15 +417,13 @@ class ModuleManager(object):
result = dict() result = dict()
state = self.want.state state = self.want.state
try:
if state == "present": if state == "present":
changed = self.present() changed = self.present()
elif state == "absent": elif state == "absent":
changed = self.absent() changed = self.absent()
except iControlUnexpectedHTTPError as e:
raise F5ModuleError(str(e))
changes = self.changes.to_return() reportable = ReportableChanges(params=self.changes.to_return())
changes = reportable.to_return()
result.update(**changes) result.update(**changes)
result.update(dict(changed=changed)) result.update(dict(changed=changed))
self._announce_deprecations(result) self._announce_deprecations(result)
@ -426,7 +432,7 @@ class ModuleManager(object):
def _announce_deprecations(self, result): def _announce_deprecations(self, result):
warnings = result.pop('__warnings', []) warnings = result.pop('__warnings', [])
for warning in warnings: for warning in warnings:
self.module.deprecate( self.client.module.deprecate(
msg=warning['msg'], msg=warning['msg'],
version=warning['version'] version=warning['version']
) )
@ -437,31 +443,20 @@ class ModuleManager(object):
else: else:
return self.create() return self.create()
def create(self):
self._set_changed_options()
if self.want.timeout is None:
self.want.update({'timeout': 16})
if self.want.interval is None:
self.want.update({'interval': 5})
if self.want.time_until_up is None:
self.want.update({'time_until_up': 0})
if self.want.ip is None:
self.want.update({'ip': '*'})
if self.want.port is None:
self.want.update({'port': '*'})
if self.want.send is None:
self.want.update({'send': 'default send string'})
if self.module.check_mode:
return True
self.create_on_device()
return True
def exists(self): def exists(self):
result = self.client.api.tm.ltm.monitor.udps.udp.exists( uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/udp/{2}".format(
name=self.want.name, self.client.provider['server'],
partition=self.want.partition self.client.provider['server_port'],
transform_name(self.want.partition, self.want.name)
) )
return result resp = self.client.api.get(uri)
try:
response = resp.json()
except ValueError:
return False
if resp.status == 404 or 'code' in response and response['code'] == 404:
return False
return True
def update(self): def update(self):
self.have = self.read_current_from_device() self.have = self.read_current_from_device()
@ -480,21 +475,66 @@ class ModuleManager(object):
raise F5ModuleError("Failed to delete the resource.") raise F5ModuleError("Failed to delete the resource.")
return True return True
def create(self):
self._set_changed_options()
self._set_default_creation_values()
if self.module.check_mode:
return True
self.create_on_device()
return True
def _set_default_creation_values(self):
if self.want.timeout is None:
self.want.update({'timeout': 16})
if self.want.interval is None:
self.want.update({'interval': 5})
if self.want.time_until_up is None:
self.want.update({'time_until_up': 0})
if self.want.ip is None:
self.want.update({'ip': '*'})
if self.want.port is None:
self.want.update({'port': '*'})
if self.want.send is None:
self.want.update({'send': 'default send string'})
def create_on_device(self): def create_on_device(self):
params = self.want.api_params() params = self.changes.api_params()
self.client.api.tm.ltm.monitor.udps.udp.create( params['name'] = self.want.name
name=self.want.name, params['partition'] = self.want.partition
partition=self.want.partition, uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/udp/".format(
**params self.client.provider['server'],
self.client.provider['server_port']
) )
resp = self.client.api.post(uri, json=params)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] in [400, 403]:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
def update_on_device(self): def update_on_device(self):
params = self.want.api_params() params = self.changes.api_params()
resource = self.client.api.tm.ltm.monitor.udps.udp.load( uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/udp/{2}".format(
name=self.want.name, self.client.provider['server'],
partition=self.want.partition self.client.provider['server_port'],
transform_name(self.want.partition, self.want.name)
) )
resource.modify(**params) resp = self.client.api.patch(uri, json=params)
try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] == 400:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
def absent(self): def absent(self):
if self.exists(): if self.exists():
@ -502,20 +542,33 @@ class ModuleManager(object):
return False return False
def remove_from_device(self): def remove_from_device(self):
resource = self.client.api.tm.ltm.monitor.udps.udp.load( uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/udp/{2}".format(
name=self.want.name, self.client.provider['server'],
partition=self.want.partition self.client.provider['server_port'],
transform_name(self.want.partition, self.want.name)
) )
if resource: resp = self.client.api.delete(uri)
resource.delete() if resp.status == 200:
return True
def read_current_from_device(self): def read_current_from_device(self):
resource = self.client.api.tm.ltm.monitor.udps.udp.load( uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/udp/{2}".format(
name=self.want.name, self.client.provider['server'],
partition=self.want.partition self.client.provider['server_port'],
transform_name(self.want.partition, self.want.name)
) )
result = resource.attrs resp = self.client.api.get(uri)
return Parameters(params=result) try:
response = resp.json()
except ValueError as ex:
raise F5ModuleError(str(ex))
if 'code' in response and response['code'] == 400:
if 'message' in response:
raise F5ModuleError(response['message'])
else:
raise F5ModuleError(resp.content)
return ApiParameters(params=response)
class ArgumentSpec(object): class ArgumentSpec(object):
@ -552,20 +605,19 @@ def main():
module = AnsibleModule( module = AnsibleModule(
argument_spec=spec.argument_spec, argument_spec=spec.argument_spec,
supports_check_mode=spec.supports_check_mode supports_check_mode=spec.supports_check_mode,
) )
if not HAS_F5SDK:
module.fail_json(msg="The python f5-sdk module is required") client = F5RestClient(**module.params)
try: try:
client = F5Client(**module.params)
mm = ModuleManager(module=module, client=client) mm = ModuleManager(module=module, client=client)
results = mm.exec_module() results = mm.exec_module()
cleanup_tokens(client) cleanup_tokens(client)
module.exit_json(**results) exit_json(module, results, client)
except F5ModuleError as ex: except F5ModuleError as ex:
cleanup_tokens(client) cleanup_tokens(client)
module.fail_json(msg=str(ex)) fail_json(module, ex, client)
if __name__ == '__main__': if __name__ == '__main__':

@ -152,7 +152,7 @@ try:
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 fq_name from library.module_utils.network.f5.common import fq_name
from library.module_utils.network.f5.common import f5_argument_spec from library.module_utils.network.f5.common import f5_argument_spec
from library.module_utils.network.f5.common import compare_dictionary from library.module_utils.network.f5.common import compare_complex_list
try: try:
from library.module_utils.network.f5.common import iControlUnexpectedHTTPError from library.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError: except ImportError:
@ -165,7 +165,7 @@ except ImportError:
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 fq_name from ansible.module_utils.network.f5.common import fq_name
from ansible.module_utils.network.f5.common import f5_argument_spec from ansible.module_utils.network.f5.common import f5_argument_spec
from ansible.module_utils.network.f5.common import compare_dictionary from ansible.module_utils.network.f5.common import compare_complex_list
try: try:
from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError
except ImportError: except ImportError:
@ -371,7 +371,7 @@ class Difference(object):
have = [tuple(x.pop('destination_ports')) for x in self.have.rules if 'destination_ports' in x] have = [tuple(x.pop('destination_ports')) for x in self.have.rules if 'destination_ports' in x]
if set(want) != set(have): if set(want) != set(have):
return self.want.rules return self.want.rules
if compare_dictionary(self.want.rules, self.have.rules): if compare_complex_list(self.want.rules, self.have.rules):
return self.want.rules return self.want.rules

Loading…
Cancel
Save