From c19a4932eb39a7f489a7e65abd3ed927fb4c00be Mon Sep 17 00:00:00 2001 From: Tim Rupp Date: Tue, 28 Aug 2018 12:18:28 -0700 Subject: [PATCH] Remove f5-sdk usage (#44781) This patch is part of an ongoing effort to remove the f5-sdk from the f5 ansible modules for dependency reasons. --- .../modules/network/f5/bigip_monitor_dns.py | 4 +- .../modules/network/f5/bigip_monitor_http.py | 282 ++++++++++------- .../modules/network/f5/bigip_monitor_https.py | 290 +++++++++++------- .../network/f5/bigip_monitor_snmp_dca.py | 288 ++++++++++------- .../modules/network/f5/bigip_monitor_tcp.py | 275 ++++++++++------- .../network/f5/bigip_monitor_tcp_echo.py | 282 ++++++++++------- .../network/f5/bigip_monitor_tcp_half_open.py | 288 ++++++++++------- .../modules/network/f5/bigip_profile_http.py | 2 + .../f5/bigip_profile_http_compression.py | 2 + 9 files changed, 1021 insertions(+), 692 deletions(-) diff --git a/lib/ansible/modules/network/f5/bigip_monitor_dns.py b/lib/ansible/modules/network/f5/bigip_monitor_dns.py index f5358c67da8..92de8287bb8 100644 --- a/lib/ansible/modules/network/f5/bigip_monitor_dns.py +++ b/lib/ansible/modules/network/f5/bigip_monitor_dns.py @@ -998,13 +998,15 @@ def main(): argument_spec=spec.argument_spec, supports_check_mode=spec.supports_check_mode, ) + client = F5RestClient(**module.params) try: - client = F5RestClient(**module.params) mm = ModuleManager(module=module, client=client) results = mm.exec_module() + cleanup_tokens(client) exit_json(module, results, client) except F5ModuleError as ex: + cleanup_tokens(client) fail_json(module, ex, client) diff --git a/lib/ansible/modules/network/f5/bigip_monitor_http.py b/lib/ansible/modules/network/f5/bigip_monitor_http.py index bf7a20bf43d..bd990ccef07 100644 --- a/lib/ansible/modules/network/f5/bigip_monitor_http.py +++ b/lib/ansible/modules/network/f5/bigip_monitor_http.py @@ -176,31 +176,29 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import env_fallback try: - 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 F5RestClient 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 fq_name 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 flatten_boolean + 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 - try: - from library.module_utils.network.f5.common import iControlUnexpectedHTTPError - except ImportError: - HAS_F5SDK = False except ImportError: - 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 F5RestClient 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 fq_name 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 flatten_boolean + 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 - try: - from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError - except ImportError: - HAS_F5SDK = False class Parameters(AnsibleF5Parameters): @@ -208,34 +206,24 @@ class Parameters(AnsibleF5Parameters): 'timeUntilUp': 'time_until_up', 'defaultsFrom': 'parent', 'recv': 'receive', - 'recvDisable': 'receive_disable' + 'recvDisable': 'receive_disable', } api_attributes = [ 'timeUntilUp', 'defaultsFrom', 'interval', 'timeout', 'recv', 'send', - 'destination', 'username', 'password', 'recvDisable', 'description' + 'destination', 'username', 'password', 'recvDisable', 'description', ] returnables = [ 'parent', 'send', 'receive', 'ip', 'port', 'interval', 'timeout', - 'time_until_up', 'receive_disable', 'description' + 'time_until_up', 'receive_disable', 'description', ] updatables = [ 'destination', 'send', 'receive', 'interval', 'timeout', 'time_until_up', - 'target_username', 'target_password', 'receive_disable', 'description' + 'target_username', 'target_password', 'receive_disable', '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 def destination(self): if self.ip is None and self.port is None: @@ -315,7 +303,31 @@ class Parameters(AnsibleF5Parameters): return self._values['target_password'] +class ApiParameters(Parameters): + pass + + +class ModuleParameters(Parameters): + pass + + 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 @@ -390,9 +402,9 @@ class ModuleManager(object): def __init__(self, *args, **kwargs): self.module = kwargs.get('module', None) self.client = kwargs.get('client', None) - self.have = None - self.want = Parameters(params=self.module.params) - self.changes = Changes() + self.want = ModuleParameters(params=self.module.params) + self.have = ApiParameters() + self.changes = UsableChanges() def _set_changed_options(self): changed = {} @@ -400,7 +412,7 @@ class ModuleManager(object): if getattr(self.want, key) is not None: changed[key] = getattr(self.want, key) if changed: - self.changes = Changes(params=changed) + self.changes = UsableChanges(params=changed) def _update_changed_options(self): diff = Difference(self.want, self.have) @@ -416,72 +428,62 @@ class ModuleManager(object): else: changed[k] = change if changed: - self.changes = Changes(params=changed) + self.changes = UsableChanges(params=changed) return True return False - def _announce_deprecations(self): - warnings = [] - if self.want: - warnings += self.want._values.get('__warnings', []) - if self.have: - warnings += self.have._values.get('__warnings', []) - for warning in warnings: - self.module.deprecate( - msg=warning['msg'], - version=warning['version'] - ) + def should_update(self): + result = self._update_changed_options() + if result: + return True + return False def exec_module(self): changed = False result = dict() state = self.want.state - try: - if state == "present": - changed = self.present() - elif state == "absent": - changed = self.absent() - except iControlUnexpectedHTTPError as e: - raise F5ModuleError(str(e)) - - changes = self.changes.to_return() + if state == "present": + changed = self.present() + elif state == "absent": + changed = self.absent() + + reportable = ReportableChanges(params=self.changes.to_return()) + changes = reportable.to_return() result.update(**changes) result.update(dict(changed=changed)) - self._announce_deprecations() + self._announce_deprecations(result) return result + def _announce_deprecations(self, result): + warnings = result.pop('__warnings', []) + for warning in warnings: + self.client.module.deprecate( + msg=warning['msg'], + version=warning['version'] + ) + def present(self): if self.exists(): return self.update() else: 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': 'GET /\r\n'}) - if self.module.check_mode: - return True - self.create_on_device() + def exists(self): + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/http/{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: + return False + if resp.status == 404 or 'code' in response and response['code'] == 404: + return False return True - def should_update(self): - result = self._update_changed_options() - if result: - return True - return False - def update(self): self.have = self.read_current_from_device() if not self.should_update(): @@ -491,11 +493,6 @@ class ModuleManager(object): self.update_on_device() return True - def absent(self): - if self.exists(): - return self.remove() - return False - def remove(self): if self.module.check_mode: return True @@ -504,44 +501,100 @@ class ModuleManager(object): raise F5ModuleError("Failed to delete the monitor.") return True - def read_current_from_device(self): - resource = self.client.api.tm.ltm.monitor.https.http.load( - name=self.want.name, - partition=self.want.partition - ) - result = resource.attrs - return Parameters(params=result) + 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 exists(self): - result = self.client.api.tm.ltm.monitor.https.http.exists( - name=self.want.name, - partition=self.want.partition + 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': 'GET /\r\n'}) + + def create_on_device(self): + params = self.changes.api_params() + params['name'] = self.want.name + params['partition'] = self.want.partition + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/http/".format( + self.client.provider['server'], + self.client.provider['server_port'] ) - return result + 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): - params = self.want.api_params() - result = self.client.api.tm.ltm.monitor.https.http.load( - name=self.want.name, - partition=self.want.partition + params = self.changes.api_params() + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/http/{2}".format( + self.client.provider['server'], + self.client.provider['server_port'], + transform_name(self.want.partition, self.want.name) ) - result.modify(**params) + resp = self.client.api.patch(uri, json=params) + try: + response = resp.json() + except ValueError as ex: + raise F5ModuleError(str(ex)) - def create_on_device(self): - params = self.want.api_params() - self.client.api.tm.ltm.monitor.https.http.create( - name=self.want.name, - partition=self.want.partition, - **params - ) + if 'code' in response and response['code'] == 400: + if 'message' in response: + raise F5ModuleError(response['message']) + else: + raise F5ModuleError(resp.content) + + def absent(self): + if self.exists(): + return self.remove() + return False def remove_from_device(self): - result = self.client.api.tm.ltm.monitor.https.http.load( - name=self.want.name, - partition=self.want.partition + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/http/{2}".format( + self.client.provider['server'], + self.client.provider['server_port'], + transform_name(self.want.partition, self.want.name) ) - if result: - result.delete() + resp = self.client.api.delete(uri) + if resp.status == 200: + return True + + def read_current_from_device(self): + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/http/{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): @@ -580,20 +633,17 @@ def main(): module = AnsibleModule( 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: - client = F5Client(**module.params) mm = ModuleManager(module=module, client=client) results = mm.exec_module() cleanup_tokens(client) - module.exit_json(**results) + exit_json(module, results, client) except F5ModuleError as ex: cleanup_tokens(client) - module.fail_json(msg=str(ex)) + fail_json(module, ex, client) if __name__ == '__main__': diff --git a/lib/ansible/modules/network/f5/bigip_monitor_https.py b/lib/ansible/modules/network/f5/bigip_monitor_https.py index 1b13b2a7867..0f61d6810e1 100644 --- a/lib/ansible/modules/network/f5/bigip_monitor_https.py +++ b/lib/ansible/modules/network/f5/bigip_monitor_https.py @@ -164,31 +164,31 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import env_fallback try: - 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 F5RestClient 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 fq_name 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 flatten_boolean + 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 - try: - from library.module_utils.network.f5.common import iControlUnexpectedHTTPError - except ImportError: - HAS_F5SDK = False + except ImportError: - 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 F5RestClient 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 fq_name 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 flatten_boolean + 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.common import f5_argument_spec 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): @@ -196,34 +196,24 @@ class Parameters(AnsibleF5Parameters): 'timeUntilUp': 'time_until_up', 'defaultsFrom': 'parent', 'recv': 'receive', - 'recvDisable': 'receive_disable' + 'recvDisable': 'receive_disable', } api_attributes = [ 'timeUntilUp', 'defaultsFrom', 'interval', 'timeout', 'recv', 'send', - 'destination', 'username', 'password', 'recvDisable', 'description' + 'destination', 'username', 'password', 'recvDisable', 'description', ] returnables = [ 'parent', 'send', 'receive', 'ip', 'port', 'interval', 'timeout', - 'time_until_up', 'receive_disable', 'description' + 'time_until_up', 'receive_disable', 'description', ] updatables = [ 'destination', 'send', 'receive', 'interval', 'timeout', 'time_until_up', - 'target_username', 'target_password', 'receive_disable', 'description' + 'target_username', 'target_password', 'receive_disable', 'description', ] - def to_return(self): - result = {} - try: - for returnable in self.returnables: - result[returnable] = getattr(self, returnable) - result = self._filter_params(result) - return result - except Exception: - return result - @property def username(self): return self._values['target_username'] @@ -302,7 +292,31 @@ class Parameters(AnsibleF5Parameters): return 'https' +class ApiParameters(Parameters): + pass + + +class ModuleParameters(Parameters): + pass + + 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 @@ -377,9 +391,9 @@ class ModuleManager(object): def __init__(self, *args, **kwargs): self.module = kwargs.get('module', None) self.client = kwargs.get('client', None) - self.have = None - self.want = Parameters(params=self.module.params) - self.changes = Changes() + self.want = ModuleParameters(params=self.module.params) + self.have = ApiParameters() + self.changes = UsableChanges() def _set_changed_options(self): changed = {} @@ -387,7 +401,7 @@ class ModuleManager(object): if getattr(self.want, key) is not None: changed[key] = getattr(self.want, key) if changed: - self.changes = Changes(params=changed) + self.changes = UsableChanges(params=changed) def _update_changed_options(self): diff = Difference(self.want, self.have) @@ -398,74 +412,67 @@ class ModuleManager(object): if change is None: continue else: - changed[k] = change + if isinstance(change, dict): + changed.update(change) + else: + changed[k] = change if changed: - self.changes = Changes(params=changed) + self.changes = UsableChanges(params=changed) return True return False - def _announce_deprecations(self): - warnings = [] - if self.want: - warnings += self.want._values.get('__warnings', []) - if self.have: - warnings += self.have._values.get('__warnings', []) - for warning in warnings: - self.module.deprecate( - msg=warning['msg'], - version=warning['version'] - ) + def should_update(self): + result = self._update_changed_options() + if result: + return True + return False def exec_module(self): changed = False result = dict() state = self.want.state - try: - if state == "present": - changed = self.present() - elif state == "absent": - changed = self.absent() - except iControlUnexpectedHTTPError as e: - raise F5ModuleError(str(e)) - - changes = self.changes.to_return() + if state == "present": + changed = self.present() + elif state == "absent": + changed = self.absent() + + reportable = ReportableChanges(params=self.changes.to_return()) + changes = reportable.to_return() result.update(**changes) result.update(dict(changed=changed)) - self._announce_deprecations() + self._announce_deprecations(result) return result + def _announce_deprecations(self, result): + warnings = result.pop('__warnings', []) + for warning in warnings: + self.client.module.deprecate( + msg=warning['msg'], + version=warning['version'] + ) + def present(self): if self.exists(): return self.update() else: 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': 'GET /\r\n'}) - if self.module.check_mode: - return True - self.create_on_device() + def exists(self): + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/https/{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: + return False + if resp.status == 404 or 'code' in response and response['code'] == 404: + return False return True - def should_update(self): - result = self._update_changed_options() - if result: - return True - return False - def update(self): self.have = self.read_current_from_device() if not self.should_update(): @@ -475,57 +482,108 @@ class ModuleManager(object): self.update_on_device() return True - def absent(self): - if self.exists(): - return self.remove() - return False - def remove(self): if self.module.check_mode: return True self.remove_from_device() if self.exists(): - raise F5ModuleError("Failed to delete the monitor.") + raise F5ModuleError("Failed to delete the resource.") return True - def read_current_from_device(self): - resource = self.client.api.tm.ltm.monitor.https_s.https.load( - name=self.want.name, - partition=self.want.partition - ) - result = resource.attrs - return Parameters(params=result) + 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 exists(self): - result = self.client.api.tm.ltm.monitor.https_s.https.exists( - name=self.want.name, - partition=self.want.partition + 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': 'GET /\r\n'}) + + def create_on_device(self): + params = self.changes.api_params() + params['name'] = self.want.name + params['partition'] = self.want.partition + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/https/".format( + self.client.provider['server'], + self.client.provider['server_port'] ) - return result + 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): - params = self.want.api_params() - result = self.client.api.tm.ltm.monitor.https_s.https.load( - name=self.want.name, - partition=self.want.partition + params = self.changes.api_params() + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/https/{2}".format( + self.client.provider['server'], + self.client.provider['server_port'], + transform_name(self.want.partition, self.want.name) ) - result.modify(**params) + resp = self.client.api.patch(uri, json=params) + try: + response = resp.json() + except ValueError as ex: + raise F5ModuleError(str(ex)) - def create_on_device(self): - params = self.want.api_params() - self.client.api.tm.ltm.monitor.https_s.https.create( - name=self.want.name, - partition=self.want.partition, - **params - ) + if 'code' in response and response['code'] == 400: + if 'message' in response: + raise F5ModuleError(response['message']) + else: + raise F5ModuleError(resp.content) + + def absent(self): + if self.exists(): + return self.remove() + return False def remove_from_device(self): - result = self.client.api.tm.ltm.monitor.https_s.https.load( - name=self.want.name, - partition=self.want.partition + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/https/{2}".format( + self.client.provider['server'], + self.client.provider['server_port'], + transform_name(self.want.partition, self.want.name) ) - if result: - result.delete() + resp = self.client.api.delete(uri) + if resp.status == 200: + return True + + def read_current_from_device(self): + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/https/{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): @@ -564,20 +622,18 @@ def main(): module = AnsibleModule( 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: - client = F5Client(**module.params) mm = ModuleManager(module=module, client=client) results = mm.exec_module() cleanup_tokens(client) - module.exit_json(**results) + exit_json(module, results, client) except F5ModuleError as ex: cleanup_tokens(client) - module.fail_json(msg=str(ex)) + fail_json(module, ex, client) if __name__ == '__main__': diff --git a/lib/ansible/modules/network/f5/bigip_monitor_snmp_dca.py b/lib/ansible/modules/network/f5/bigip_monitor_snmp_dca.py index 079dee61d77..084f35b0918 100644 --- a/lib/ansible/modules/network/f5/bigip_monitor_snmp_dca.py +++ b/lib/ansible/modules/network/f5/bigip_monitor_snmp_dca.py @@ -229,29 +229,28 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import env_fallback try: - 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 F5RestClient 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 fq_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 + from library.module_utils.network.f5.common import transform_name + from library.module_utils.network.f5.common import flatten_boolean + from library.module_utils.network.f5.common import exit_json + from library.module_utils.network.f5.common import fail_json + except ImportError: - 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 F5RestClient 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 fq_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 + from ansible.module_utils.network.f5.common import transform_name + from ansible.module_utils.network.f5.common import flatten_boolean + from ansible.module_utils.network.f5.common import exit_json + from ansible.module_utils.network.f5.common import fail_json class Parameters(AnsibleF5Parameters): @@ -264,37 +263,27 @@ class Parameters(AnsibleF5Parameters): 'memoryCoefficient': 'memory_coefficient', 'memoryThreshold': 'memory_threshold', 'diskCoefficient': 'disk_coefficient', - 'diskThreshold': 'disk_threshold' + 'diskThreshold': 'disk_threshold', } api_attributes = [ 'timeUntilUp', 'defaultsFrom', 'interval', 'timeout', 'destination', 'community', 'version', 'agentType', 'cpuCoefficient', 'cpuThreshold', 'memoryCoefficient', - 'memoryThreshold', 'diskCoefficient', 'diskThreshold', 'description' + 'memoryThreshold', 'diskCoefficient', 'diskThreshold', 'description', ] returnables = [ 'parent', 'ip', 'interval', 'timeout', 'time_until_up', 'description', 'community', 'version', 'agent_type', 'cpu_coefficient', 'cpu_threshold', 'memory_coefficient', - 'memory_threshold', 'disk_coefficient', 'disk_threshold' + 'memory_threshold', 'disk_coefficient', 'disk_threshold', ] updatables = [ 'ip', 'interval', 'timeout', 'time_until_up', 'description', 'community', 'version', 'agent_type', 'cpu_coefficient', 'cpu_threshold', 'memory_coefficient', - 'memory_threshold', 'disk_coefficient', 'disk_threshold' + 'memory_threshold', 'disk_coefficient', 'disk_threshold', ] - def to_return(self): - result = {} - try: - for returnable in self.returnables: - result[returnable] = getattr(self, returnable) - result = self._filter_params(result) - return result - except Exception: - return result - @property def interval(self): if self._values['interval'] is None: @@ -370,7 +359,31 @@ class Parameters(AnsibleF5Parameters): return 'snmp_dca' +class ApiParameters(Parameters): + pass + + +class ModuleParameters(Parameters): + pass + + 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 @@ -428,9 +441,9 @@ class ModuleManager(object): def __init__(self, *args, **kwargs): self.module = kwargs.get('module', None) self.client = kwargs.get('client', None) - self.have = None - self.want = Parameters(params=self.module.params) - self.changes = Changes() + self.want = ModuleParameters(params=self.module.params) + self.have = ApiParameters() + self.changes = UsableChanges() def _set_changed_options(self): changed = {} @@ -438,7 +451,7 @@ class ModuleManager(object): if getattr(self.want, key) is not None: changed[key] = getattr(self.want, key) if changed: - self.changes = Changes(params=changed) + self.changes = UsableChanges(params=changed) def _update_changed_options(self): diff = Difference(self.want, self.have) @@ -449,52 +462,93 @@ class ModuleManager(object): if change is None: continue else: - changed[k] = change + if isinstance(change, dict): + changed.update(change) + else: + changed[k] = change if changed: - self.changes = Changes(params=changed) + self.changes = UsableChanges(params=changed) return True return False - def _announce_deprecations(self): - warnings = [] - if self.want: - warnings += self.want._values.get('__warnings', []) - if self.have: - warnings += self.have._values.get('__warnings', []) - for warning in warnings: - self.module.deprecate( - msg=warning['msg'], - version=warning['version'] - ) + def should_update(self): + result = self._update_changed_options() + if result: + return True + return False def exec_module(self): changed = False result = dict() state = self.want.state - try: - if state == "present": - changed = self.present() - elif state == "absent": - changed = self.absent() - except iControlUnexpectedHTTPError as e: - raise F5ModuleError(str(e)) - - changes = self.changes.to_return() + if state == "present": + changed = self.present() + elif state == "absent": + changed = self.absent() + + reportable = ReportableChanges(params=self.changes.to_return()) + changes = reportable.to_return() result.update(**changes) result.update(dict(changed=changed)) - self._announce_deprecations() + self._announce_deprecations(result) return result + def _announce_deprecations(self, result): + warnings = result.pop('__warnings', []) + for warning in warnings: + self.client.module.deprecate( + msg=warning['msg'], + version=warning['version'] + ) + def present(self): if self.exists(): return self.update() else: return self.create() + def exists(self): + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/snmp-dca/{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: + return False + if resp.status == 404 or 'code' in response and response['code'] == 404: + return False + return True + + 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 remove(self): + if self.module.check_mode: + return True + self.remove_from_device() + if self.exists(): + raise F5ModuleError("Failed to delete the resource.") + 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': 30}) if self.want.interval is None: @@ -520,77 +574,78 @@ class ModuleManager(object): if self.want.disk_threshold is None: self.want.update({'disk_threshold': '90'}) - if self.module.check_mode: - return True - self.create_on_device() - return True + def create_on_device(self): + params = self.changes.api_params() + params['name'] = self.want.name + params['partition'] = self.want.partition + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/snmp-dca/".format( + 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)) - def should_update(self): - result = self._update_changed_options() - if result: - return True - return False + 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(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 update_on_device(self): + params = self.changes.api_params() + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/snmp-dca/{2}".format( + self.client.provider['server'], + self.client.provider['server_port'], + transform_name(self.want.partition, self.want.name) + ) + 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): if self.exists(): return self.remove() return False - def remove(self): - if self.module.check_mode: + def remove_from_device(self): + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/snmp-dca/{2}".format( + self.client.provider['server'], + self.client.provider['server_port'], + transform_name(self.want.partition, self.want.name) + ) + resp = self.client.api.delete(uri) + if resp.status == 200: return True - self.remove_from_device() - if self.exists(): - raise F5ModuleError("Failed to delete the monitor.") - return True def read_current_from_device(self): - resource = self.client.api.tm.ltm.monitor.snmp_dcas.snmp_dca.load( - name=self.want.name, - partition=self.want.partition - ) - result = resource.attrs - return Parameters(params=result) - - def exists(self): - result = self.client.api.tm.ltm.monitor.snmp_dcas.snmp_dca.exists( - name=self.want.name, - partition=self.want.partition - ) - return result - - def update_on_device(self): - params = self.want.api_params() - result = self.client.api.tm.ltm.monitor.snmp_dcas.snmp_dca.load( - name=self.want.name, - partition=self.want.partition - ) - result.modify(**params) - - def create_on_device(self): - params = self.want.api_params() - self.client.api.tm.ltm.monitor.snmp_dcas.snmp_dca.create( - name=self.want.name, - partition=self.want.partition, - **params + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/snmp-dca/{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)) - def remove_from_device(self): - result = self.client.api.tm.ltm.monitor.snmp_dcas.snmp_dca.load( - name=self.want.name, - partition=self.want.partition - ) - if result: - result.delete() + 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): @@ -633,20 +688,19 @@ def main(): module = AnsibleModule( 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: - client = F5Client(**module.params) mm = ModuleManager(module=module, client=client) results = mm.exec_module() cleanup_tokens(client) - module.exit_json(**results) + exit_json(module, results, client) except F5ModuleError as ex: cleanup_tokens(client) - module.fail_json(msg=str(ex)) + fail_json(module, ex, client) if __name__ == '__main__': diff --git a/lib/ansible/modules/network/f5/bigip_monitor_tcp.py b/lib/ansible/modules/network/f5/bigip_monitor_tcp.py index b9bee763af7..07d475a16a2 100644 --- a/lib/ansible/modules/network/f5/bigip_monitor_tcp.py +++ b/lib/ansible/modules/network/f5/bigip_monitor_tcp.py @@ -172,75 +172,54 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import env_fallback try: - 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 F5RestClient 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 fq_name 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 flatten_boolean + 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 - try: - from library.module_utils.network.f5.common import iControlUnexpectedHTTPError - except ImportError: - HAS_F5SDK = False + except ImportError: - 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 F5RestClient 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 fq_name 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 flatten_boolean + 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 - try: - from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError - except ImportError: - HAS_F5SDK = False class Parameters(AnsibleF5Parameters): api_map = { 'timeUntilUp': 'time_until_up', 'defaultsFrom': 'parent', - 'recv': 'receive' + 'recv': 'receive', } api_attributes = [ 'timeUntilUp', 'defaultsFrom', 'interval', 'timeout', 'recv', 'send', - 'destination', 'description' + 'destination', 'description', ] returnables = [ 'parent', 'send', 'receive', 'ip', 'port', 'interval', 'timeout', - 'time_until_up', 'description' + 'time_until_up', 'description', ] updatables = [ '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) - return result - except Exception: - return result - - def api_params(self): - result = {} - for api_attribute in self.api_attributes: - if self.api_map is not None and api_attribute in self.api_map: - result[api_attribute] = getattr(self, self.api_map[api_attribute]) - else: - result[api_attribute] = getattr(self, api_attribute) - result = self._filter_params(result) - return result - @property def interval(self): if self._values['interval'] is None: @@ -309,7 +288,31 @@ class Parameters(AnsibleF5Parameters): return 'tcp' +class ApiParameters(Parameters): + pass + + +class ModuleParameters(Parameters): + pass + + 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 @@ -384,9 +387,9 @@ class ModuleManager(object): def __init__(self, *args, **kwargs): self.module = kwargs.get('module', None) self.client = kwargs.get('client', None) - self.have = None - self.want = Parameters(params=self.module.params) - self.changes = Changes() + self.want = ModuleParameters(params=self.module.params) + self.have = ApiParameters() + self.changes = UsableChanges() def _set_changed_options(self): changed = {} @@ -394,7 +397,7 @@ class ModuleManager(object): if getattr(self.want, key) is not None: changed[key] = getattr(self.want, key) if changed: - self.changes = Changes(params=changed) + self.changes = UsableChanges(params=changed) def _update_changed_options(self): diff = Difference(self.want, self.have) @@ -405,63 +408,67 @@ class ModuleManager(object): if change is None: continue else: - changed[k] = change + if isinstance(change, dict): + changed.update(change) + else: + changed[k] = change if changed: - self.changes = Changes(params=changed) + self.changes = UsableChanges(params=changed) return True return False - def _announce_deprecations(self): - warnings = [] - if self.want: - warnings += self.want._values.get('__warnings', []) - if self.have: - warnings += self.have._values.get('__warnings', []) - for warning in warnings: - self.module.deprecate( - msg=warning['msg'], - version=warning['version'] - ) + def should_update(self): + result = self._update_changed_options() + if result: + return True + return False def exec_module(self): changed = False result = dict() state = self.want.state - try: - if state == "present": - changed = self.present() - elif state == "absent": - changed = self.absent() - except iControlUnexpectedHTTPError as e: - raise F5ModuleError(str(e)) - - changes = self.changes.to_return() + if state == "present": + changed = self.present() + elif state == "absent": + changed = self.absent() + + reportable = ReportableChanges(params=self.changes.to_return()) + changes = reportable.to_return() result.update(**changes) result.update(dict(changed=changed)) - self._announce_deprecations() + self._announce_deprecations(result) return result + def _announce_deprecations(self, result): + warnings = result.pop('__warnings', []) + for warning in warnings: + self.client.module.deprecate( + msg=warning['msg'], + version=warning['version'] + ) + def present(self): if self.exists(): return self.update() else: return self.create() - def create(self): - self._set_changed_options() - self._set_default_creation_values() - if self.module.check_mode: - return True - self.create_on_device() + def exists(self): + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp/{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: + return False + if resp.status == 404 or 'code' in response and response['code'] == 404: + return False return True - def should_update(self): - result = self._update_changed_options() - if result: - return True - return False - def update(self): self.have = self.read_current_from_device() if not self.should_update(): @@ -471,9 +478,10 @@ class ModuleManager(object): self.update_on_device() return True - def absent(self): - if self.exists(): - return self.remove() + def should_update(self): + result = self._update_changed_options() + if result: + return True return False def remove(self): @@ -481,7 +489,15 @@ class ModuleManager(object): return True self.remove_from_device() if self.exists(): - raise F5ModuleError("Failed to delete the monitor.") + raise F5ModuleError("Failed to delete the resource.") + 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): @@ -496,44 +512,78 @@ class ModuleManager(object): if self.want.port is None: self.want.update({'port': '*'}) - def read_current_from_device(self): - resource = self.client.api.tm.ltm.monitor.tcps.tcp.load( - name=self.want.name, - partition=self.want.partition + def create_on_device(self): + params = self.changes.api_params() + params['name'] = self.want.name + params['partition'] = self.want.partition + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp/".format( + self.client.provider['server'], + self.client.provider['server_port'] ) - result = resource.attrs - return Parameters(params=result) + resp = self.client.api.post(uri, json=params) + try: + response = resp.json() + except ValueError as ex: + raise F5ModuleError(str(ex)) - def exists(self): - result = self.client.api.tm.ltm.monitor.tcps.tcp.exists( - name=self.want.name, - partition=self.want.partition - ) - return result + 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): params = self.changes.api_params() - result = self.client.api.tm.ltm.monitor.tcps.tcp.load( - name=self.want.name, - partition=self.want.partition + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp/{2}".format( + self.client.provider['server'], + self.client.provider['server_port'], + transform_name(self.want.partition, self.want.name) ) - result.modify(**params) + resp = self.client.api.patch(uri, json=params) + try: + response = resp.json() + except ValueError as ex: + raise F5ModuleError(str(ex)) - def create_on_device(self): - params = self.want.api_params() - self.client.api.tm.ltm.monitor.tcps.tcp.create( - name=self.want.name, - partition=self.want.partition, - **params - ) + if 'code' in response and response['code'] == 400: + if 'message' in response: + raise F5ModuleError(response['message']) + else: + raise F5ModuleError(resp.content) + + def absent(self): + if self.exists(): + return self.remove() + return False def remove_from_device(self): - result = self.client.api.tm.ltm.monitor.tcps.tcp.load( - name=self.want.name, - partition=self.want.partition + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp/{2}".format( + self.client.provider['server'], + self.client.provider['server_port'], + transform_name(self.want.partition, self.want.name) ) - if result: - result.delete() + resp = self.client.api.delete(uri) + if resp.status == 200: + return True + + def read_current_from_device(self): + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp/{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): @@ -569,20 +619,19 @@ def main(): module = AnsibleModule( 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: - client = F5Client(**module.params) mm = ModuleManager(module=module, client=client) results = mm.exec_module() cleanup_tokens(client) - module.exit_json(**results) + exit_json(module, results, client) except F5ModuleError as ex: cleanup_tokens(client) - module.fail_json(msg=str(ex)) + fail_json(module, ex, client) if __name__ == '__main__': diff --git a/lib/ansible/modules/network/f5/bigip_monitor_tcp_echo.py b/lib/ansible/modules/network/f5/bigip_monitor_tcp_echo.py index ff541771be7..724c28d2901 100644 --- a/lib/ansible/modules/network/f5/bigip_monitor_tcp_echo.py +++ b/lib/ansible/modules/network/f5/bigip_monitor_tcp_echo.py @@ -144,60 +144,50 @@ from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import env_fallback try: - 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 F5RestClient 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 fq_name 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 flatten_boolean + 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 - try: - from library.module_utils.network.f5.common import iControlUnexpectedHTTPError - except ImportError: - HAS_F5SDK = False except ImportError: - 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 F5RestClient 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 fq_name 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 flatten_boolean + 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 - try: - from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError - except ImportError: - HAS_F5SDK = False class Parameters(AnsibleF5Parameters): api_map = { 'timeUntilUp': 'time_until_up', - 'defaultsFrom': 'parent' + 'defaultsFrom': 'parent', } api_attributes = [ 'timeUntilUp', 'defaultsFrom', 'interval', 'timeout', 'destination', - 'description' + 'description', ] returnables = [ - 'parent', 'ip', 'interval', 'timeout', 'time_until_up', 'description' + 'parent', 'ip', 'interval', 'timeout', 'time_until_up', 'description', ] updatables = [ - 'ip', 'interval', 'timeout', 'time_until_up', 'description' + 'ip', 'interval', 'timeout', 'time_until_up', 'description', ] - def to_return(self): - result = {} - try: - for returnable in self.returnables: - result[returnable] = getattr(self, returnable) - result = self._filter_params(result) - return result - except Exception: - return result - @property def interval(self): if self._values['interval'] is None: @@ -257,7 +247,31 @@ class Parameters(AnsibleF5Parameters): return 'tcp_echo' +class ApiParameters(Parameters): + pass + + +class ModuleParameters(Parameters): + pass + + 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 @@ -322,9 +336,9 @@ class ModuleManager(object): def __init__(self, *args, **kwargs): self.module = kwargs.get('module', None) self.client = kwargs.get('client', None) - self.have = None - self.want = Parameters(params=self.module.params) - self.changes = Changes() + self.want = ModuleParameters(params=self.module.params) + self.have = ApiParameters() + self.changes = UsableChanges() def _set_changed_options(self): changed = {} @@ -332,7 +346,7 @@ class ModuleManager(object): if getattr(self.want, key) is not None: changed[key] = getattr(self.want, key) if changed: - self.changes = Changes(params=changed) + self.changes = UsableChanges(params=changed) def _update_changed_options(self): diff = Difference(self.want, self.have) @@ -343,70 +357,67 @@ class ModuleManager(object): if change is None: continue else: - changed[k] = change + if isinstance(change, dict): + changed.update(change) + else: + changed[k] = change if changed: - self.changes = Changes(params=changed) + self.changes = UsableChanges(params=changed) return True return False - def _announce_deprecations(self): - warnings = [] - if self.want: - warnings += self.want._values.get('__warnings', []) - if self.have: - warnings += self.have._values.get('__warnings', []) - for warning in warnings: - self.module.deprecate( - msg=warning['msg'], - version=warning['version'] - ) + def should_update(self): + result = self._update_changed_options() + if result: + return True + return False def exec_module(self): changed = False result = dict() state = self.want.state - try: - if state == "present": - changed = self.present() - elif state == "absent": - changed = self.absent() - except iControlUnexpectedHTTPError as e: - raise F5ModuleError(str(e)) - - changes = self.changes.to_return() + if state == "present": + changed = self.present() + elif state == "absent": + changed = self.absent() + + reportable = ReportableChanges(params=self.changes.to_return()) + changes = reportable.to_return() result.update(**changes) result.update(dict(changed=changed)) - self._announce_deprecations() + self._announce_deprecations(result) return result + def _announce_deprecations(self, result): + warnings = result.pop('__warnings', []) + for warning in warnings: + self.client.module.deprecate( + msg=warning['msg'], + version=warning['version'] + ) + def present(self): if self.exists(): return self.update() else: 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.module.check_mode: - return True - self.create_on_device() + def exists(self): + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp-echo/{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: + return False + if resp.status == 404 or 'code' in response and response['code'] == 404: + return False return True - def should_update(self): - result = self._update_changed_options() - if result: - return True - return False - def update(self): self.have = self.read_current_from_device() if not self.should_update(): @@ -416,57 +427,104 @@ class ModuleManager(object): self.update_on_device() return True - def absent(self): - if self.exists(): - return self.remove() - return False - def remove(self): if self.module.check_mode: return True self.remove_from_device() if self.exists(): - raise F5ModuleError("Failed to delete the monitor.") + raise F5ModuleError("Failed to delete the resource.") return True - def read_current_from_device(self): - resource = self.client.api.tm.ltm.monitor.tcp_echos.tcp_echo.load( - name=self.want.name, - partition=self.want.partition - ) - result = resource.attrs - return Parameters(params=result) + 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 exists(self): - result = self.client.api.tm.ltm.monitor.tcp_echos.tcp_echo.exists( - name=self.want.name, - partition=self.want.partition + 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': '*'}) + + def create_on_device(self): + params = self.changes.api_params() + params['name'] = self.want.name + params['partition'] = self.want.partition + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp-echo/".format( + self.client.provider['server'], + self.client.provider['server_port'] ) - return result + 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): - params = self.want.api_params() - result = self.client.api.tm.ltm.monitor.tcp_echos.tcp_echo.load( - name=self.want.name, - partition=self.want.partition + params = self.changes.api_params() + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp-echo/{2}".format( + self.client.provider['server'], + self.client.provider['server_port'], + transform_name(self.want.partition, self.want.name) ) - result.modify(**params) + resp = self.client.api.patch(uri, json=params) + try: + response = resp.json() + except ValueError as ex: + raise F5ModuleError(str(ex)) - def create_on_device(self): - params = self.want.api_params() - self.client.api.tm.ltm.monitor.tcp_echos.tcp_echo.create( - name=self.want.name, - partition=self.want.partition, - **params - ) + if 'code' in response and response['code'] == 400: + if 'message' in response: + raise F5ModuleError(response['message']) + else: + raise F5ModuleError(resp.content) + + def absent(self): + if self.exists(): + return self.remove() + return False def remove_from_device(self): - result = self.client.api.tm.ltm.monitor.tcp_echos.tcp_echo.load( - name=self.want.name, - partition=self.want.partition + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp-echo/{2}".format( + self.client.provider['server'], + self.client.provider['server_port'], + transform_name(self.want.partition, self.want.name) ) - if result: - result.delete() + resp = self.client.api.delete(uri) + if resp.status == 200: + return True + + def read_current_from_device(self): + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp-echo/{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): @@ -499,20 +557,18 @@ def main(): module = AnsibleModule( 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: - client = F5Client(**module.params) mm = ModuleManager(module=module, client=client) results = mm.exec_module() cleanup_tokens(client) - module.exit_json(**results) + exit_json(module, results, client) except F5ModuleError as ex: cleanup_tokens(client) - module.fail_json(msg=str(ex)) + fail_json(module, ex, client) if __name__ == '__main__': diff --git a/lib/ansible/modules/network/f5/bigip_monitor_tcp_half_open.py b/lib/ansible/modules/network/f5/bigip_monitor_tcp_half_open.py index 9c7bfbd5639..4762493b280 100644 --- a/lib/ansible/modules/network/f5/bigip_monitor_tcp_half_open.py +++ b/lib/ansible/modules/network/f5/bigip_monitor_tcp_half_open.py @@ -159,63 +159,54 @@ import os from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import env_fallback + try: - 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 F5RestClient 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 fq_name 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 flatten_boolean + 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 - try: - from library.module_utils.network.f5.common import iControlUnexpectedHTTPError - except ImportError: - HAS_F5SDK = False except ImportError: - 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 F5RestClient 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 fq_name 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 flatten_boolean + 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 - try: - from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError - except ImportError: - HAS_F5SDK = False class Parameters(AnsibleF5Parameters): api_map = { 'timeUntilUp': 'time_until_up', 'defaultsFrom': 'parent', - 'recv': 'receive' + 'recv': 'receive', } api_attributes = [ 'timeUntilUp', 'defaultsFrom', 'interval', 'timeout', 'destination', - 'description' + 'description', ] returnables = [ 'parent', 'ip', 'port', 'interval', 'timeout', 'time_until_up', - 'description' + 'description', ] updatables = [ - 'destination', 'interval', 'timeout', 'time_until_up', 'description' + 'destination', 'interval', 'timeout', 'time_until_up', 'description', ] - def to_return(self): - result = {} - try: - for returnable in self.returnables: - result[returnable] = getattr(self, returnable) - result = self._filter_params(result) - return result - except Exception: - return result - @property def destination(self): if self.ip is None and self.port is None: @@ -290,7 +281,31 @@ class Parameters(AnsibleF5Parameters): return 'tcp_half_open' +class ApiParameters(Parameters): + pass + + +class ModuleParameters(Parameters): + pass + + 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 @@ -365,9 +380,9 @@ class ModuleManager(object): def __init__(self, *args, **kwargs): self.module = kwargs.get('module', None) self.client = kwargs.get('client', None) - self.have = None - self.want = Parameters(params=self.module.params) - self.changes = Changes() + self.want = ModuleParameters(params=self.module.params) + self.have = ApiParameters() + self.changes = UsableChanges() def _set_changed_options(self): changed = {} @@ -375,7 +390,7 @@ class ModuleManager(object): if getattr(self.want, key) is not None: changed[key] = getattr(self.want, key) if changed: - self.changes = Changes(params=changed) + self.changes = UsableChanges(params=changed) def _update_changed_options(self): diff = Difference(self.want, self.have) @@ -386,72 +401,67 @@ class ModuleManager(object): if change is None: continue else: - changed[k] = change + if isinstance(change, dict): + changed.update(change) + else: + changed[k] = change if changed: - self.changes = Changes(params=changed) + self.changes = UsableChanges(params=changed) return True return False - def _announce_deprecations(self): - warnings = [] - if self.want: - warnings += self.want._values.get('__warnings', []) - if self.have: - warnings += self.have._values.get('__warnings', []) - for warning in warnings: - self.module.deprecate( - msg=warning['msg'], - version=warning['version'] - ) + def should_update(self): + result = self._update_changed_options() + if result: + return True + return False def exec_module(self): changed = False result = dict() state = self.want.state - try: - if state == "present": - changed = self.present() - elif state == "absent": - changed = self.absent() - except iControlUnexpectedHTTPError as e: - raise F5ModuleError(str(e)) - - changes = self.changes.to_return() + if state == "present": + changed = self.present() + elif state == "absent": + changed = self.absent() + + reportable = ReportableChanges(params=self.changes.to_return()) + changes = reportable.to_return() result.update(**changes) result.update(dict(changed=changed)) - self._announce_deprecations() + self._announce_deprecations(result) return result + def _announce_deprecations(self, result): + warnings = result.pop('__warnings', []) + for warning in warnings: + self.client.module.deprecate( + msg=warning['msg'], + version=warning['version'] + ) + def present(self): if self.exists(): return self.update() else: 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.module.check_mode: - return True - self.create_on_device() + def exists(self): + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp-half-open/{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: + return False + if resp.status == 404 or 'code' in response and response['code'] == 404: + return False return True - def should_update(self): - result = self._update_changed_options() - if result: - return True - return False - def update(self): self.have = self.read_current_from_device() if not self.should_update(): @@ -461,57 +471,106 @@ class ModuleManager(object): self.update_on_device() return True - def absent(self): - if self.exists(): - return self.remove() - return False - def remove(self): if self.module.check_mode: return True self.remove_from_device() if self.exists(): - raise F5ModuleError("Failed to delete the monitor.") + raise F5ModuleError("Failed to delete the resource.") return True - def read_current_from_device(self): - resource = self.client.api.tm.ltm.monitor.tcp_half_opens.tcp_half_open.load( - name=self.want.name, - partition=self.want.partition - ) - result = resource.attrs - return Parameters(params=result) + 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 exists(self): - result = self.client.api.tm.ltm.monitor.tcp_half_opens.tcp_half_open.exists( - name=self.want.name, - partition=self.want.partition + 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': '*'}) + + def create_on_device(self): + params = self.changes.api_params() + params['name'] = self.want.name + params['partition'] = self.want.partition + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp-half-open/".format( + self.client.provider['server'], + self.client.provider['server_port'] ) - return result + 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): - params = self.want.api_params() - result = self.client.api.tm.ltm.monitor.tcp_half_opens.tcp_half_open.load( - name=self.want.name, - partition=self.want.partition + params = self.changes.api_params() + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp-half-open/{2}".format( + self.client.provider['server'], + self.client.provider['server_port'], + transform_name(self.want.partition, self.want.name) ) - result.modify(**params) + resp = self.client.api.patch(uri, json=params) + try: + response = resp.json() + except ValueError as ex: + raise F5ModuleError(str(ex)) - def create_on_device(self): - params = self.want.api_params() - self.client.api.tm.ltm.monitor.tcp_half_opens.tcp_half_open.create( - name=self.want.name, - partition=self.want.partition, - **params - ) + if 'code' in response and response['code'] == 400: + if 'message' in response: + raise F5ModuleError(response['message']) + else: + raise F5ModuleError(resp.content) + + def absent(self): + if self.exists(): + return self.remove() + return False def remove_from_device(self): - result = self.client.api.tm.ltm.monitor.tcp_half_opens.tcp_half_open.load( - name=self.want.name, - partition=self.want.partition + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp-half-open/{2}".format( + self.client.provider['server'], + self.client.provider['server_port'], + transform_name(self.want.partition, self.want.name) ) - if result: - result.delete() + resp = self.client.api.delete(uri) + if resp.status == 200: + return True + + def read_current_from_device(self): + uri = "https://{0}:{1}/mgmt/tm/ltm/monitor/tcp-half-open/{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): @@ -545,20 +604,19 @@ def main(): module = AnsibleModule( 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: - client = F5Client(**module.params) mm = ModuleManager(module=module, client=client) results = mm.exec_module() cleanup_tokens(client) - module.exit_json(**results) + exit_json(module, results, client) except F5ModuleError as ex: cleanup_tokens(client) - module.fail_json(msg=str(ex)) + fail_json(module, ex, client) if __name__ == '__main__': diff --git a/lib/ansible/modules/network/f5/bigip_profile_http.py b/lib/ansible/modules/network/f5/bigip_profile_http.py index 0455ee5329e..0af19f9b028 100644 --- a/lib/ansible/modules/network/f5/bigip_profile_http.py +++ b/lib/ansible/modules/network/f5/bigip_profile_http.py @@ -672,8 +672,10 @@ def main(): try: mm = ModuleManager(module=module, client=client) results = mm.exec_module() + cleanup_tokens(client) exit_json(module, results, client) except F5ModuleError as ex: + cleanup_tokens(client) fail_json(module, ex, client) diff --git a/lib/ansible/modules/network/f5/bigip_profile_http_compression.py b/lib/ansible/modules/network/f5/bigip_profile_http_compression.py index 1bb29ef22b7..b32a6d73e30 100644 --- a/lib/ansible/modules/network/f5/bigip_profile_http_compression.py +++ b/lib/ansible/modules/network/f5/bigip_profile_http_compression.py @@ -531,8 +531,10 @@ def main(): client = F5RestClient(**module.params) mm = ModuleManager(module=module, client=client) results = mm.exec_module() + cleanup_tokens(client) exit_json(module, results, client) except F5ModuleError as ex: + cleanup_tokens(client) fail_json(module, ex, client)