From e6fad87e203e6a721f79a7ae57fec597a70ecc4f Mon Sep 17 00:00:00 2001 From: Tim Rupp Date: Fri, 9 Nov 2018 21:37:48 -0800 Subject: [PATCH] Removes the f5-sdk from bigip_config (#48456) --- .../modules/network/f5/bigip_config.py | 202 ++++++++++++------ .../modules/network/f5/test_bigip_config.py | 21 +- 2 files changed, 154 insertions(+), 69 deletions(-) diff --git a/lib/ansible/modules/network/f5/bigip_config.py b/lib/ansible/modules/network/f5/bigip_config.py index 4d55a1151ee..92722f9d9fa 100644 --- a/lib/ansible/modules/network/f5/bigip_config.py +++ b/lib/ansible/modules/network/f5/bigip_config.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # -# Copyright (c) 2017 F5 Networks Inc. +# Copyright: (c) 2017, F5 Networks Inc. # GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function @@ -68,29 +68,29 @@ EXAMPLES = r''' - name: Save the running configuration of the BIG-IP bigip_config: save: yes - server: lb.mydomain.com - password: secret - user: admin - validate_certs: no + provider: + server: lb.mydomain.com + password: secret + user: admin delegate_to: localhost - name: Reset the BIG-IP configuration, for example, to RMA the device bigip_config: reset: yes save: yes - server: lb.mydomain.com - password: secret - user: admin - validate_certs: no + provider: + server: lb.mydomain.com + password: secret + user: admin delegate_to: localhost - name: Load an SCF configuration bigip_config: merge_content: "{{ lookup('file', '/path/to/config.scf') }}" - server: lb.mydomain.com - password: secret - user: admin - validate_certs: no + provider: + server: lb.mydomain.com + password: secret + user: admin delegate_to: localhost ''' @@ -107,38 +107,36 @@ stdout_lines: sample: [['...', '...'], ['...'], ['...']] ''' +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + import os import tempfile from ansible.module_utils.basic import AnsibleModule 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 exit_json + from library.module_utils.network.f5.common import fail_json + from library.module_utils.network.f5.icontrol import upload_file 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 - -try: - from StringIO import StringIO -except ImportError: - from io import StringIO + 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.icontrol import upload_file class Parameters(AnsibleF5Parameters): @@ -177,10 +175,8 @@ class ModuleManager(object): def exec_module(self): result = {} - try: - changed = self.execute() - except iControlUnexpectedHTTPError as e: - raise F5ModuleError(str(e)) + + changed = self.execute() result.update(**self.changes.to_return()) result.update(dict(changed=changed)) @@ -231,13 +227,29 @@ class ModuleManager(object): def reset_device(self): command = 'tmsh load sys config default' - output = self.client.api.tm.util.bash.exec_cmd( - 'run', + uri = "https://{0}:{1}/mgmt/tm/util/bash".format( + self.client.provider['server'], + self.client.provider['server_port'], + ) + args = dict( + command='run', utilCmdArgs='-c "{0}"'.format(command) ) - if hasattr(output, 'commandResult'): - return str(output.commandResult) - return None + 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) + + if 'commandResult' in response: + return str(response['commandResult']) def merge(self, verify=True): temp_name = next(tempfile._get_candidate_names()) @@ -256,40 +268,94 @@ class ModuleManager(object): return response def merge_on_device(self, remote_path, verify=True): - result = None - command = 'tmsh load sys config file {0} merge'.format( remote_path ) if verify: command += ' verify' - output = self.client.api.tm.util.bash.exec_cmd( - 'run', + uri = "https://{0}:{1}/mgmt/tm/util/bash".format( + self.client.provider['server'], + self.client.provider['server_port'], + ) + args = dict( + command='run', utilCmdArgs='-c "{0}"'.format(command) ) - if hasattr(output, 'commandResult'): - result = str(output.commandResult) - return result + 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) + + if 'commandResult' in response: + return str(response['commandResult']) def remove_temporary_file(self, remote_path): - self.client.api.tm.util.unix_rm.exec_cmd( - 'run', + uri = "https://{0}:{1}/mgmt/tm/util/unix-rm".format( + self.client.provider['server'], + self.client.provider['server_port'], + ) + args = dict( + command='run', utilCmdArgs=remote_path ) + 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 move_on_device(self, remote_path): - self.client.api.tm.util.unix_mv.exec_cmd( - 'run', + uri = "https://{0}:{1}/mgmt/tm/util/unix-mv".format( + self.client.provider['server'], + self.client.provider['server_port'], + ) + args = dict( + command='run', utilCmdArgs='{0} /tmp/{1}'.format( remote_path, os.path.basename(remote_path) ) ) + 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 upload_to_device(self, temp_name): template = StringIO(self.want.merge_content) - upload = self.client.api.shared.file_transfer.uploads - upload.upload_stringio(template, temp_name) + url = 'https://{0}:{1}/mgmt/shared/file-transfer/uploads'.format( + self.client.provider['server'], + self.client.provider['server_port'] + ) + try: + upload_file(self.client, url, template, temp_name) + except F5ModuleError: + raise F5ModuleError( + "Failed to upload the file." + ) def save(self): if self.module.check_mode: @@ -297,15 +363,30 @@ class ModuleManager(object): return self.save_on_device() def save_on_device(self): - result = None command = 'tmsh save sys config' - output = self.client.api.tm.util.bash.exec_cmd( - 'run', + uri = "https://{0}:{1}/mgmt/tm/util/bash".format( + self.client.provider['server'], + self.client.provider['server_port'], + ) + args = dict( + command='run', utilCmdArgs='-c "{0}"'.format(command) ) - if hasattr(output, 'commandResult'): - result = str(output.commandResult) - return result + 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) + + if 'commandResult' in response: + return str(response['commandResult']) class ArgumentSpec(object): @@ -338,11 +419,10 @@ def main(): argument_spec=spec.argument_spec, supports_check_mode=spec.supports_check_mode ) - if not HAS_F5SDK: - module.fail_json(msg="The python f5-sdk module is required") + + client = F5RestClient(**module.params) try: - client = F5Client(**module.params) mm = ModuleManager(module=module, client=client) results = mm.exec_module() cleanup_tokens(client) diff --git a/test/units/modules/network/f5/test_bigip_config.py b/test/units/modules/network/f5/test_bigip_config.py index a4dcc4fb98e..0b706177019 100644 --- a/test/units/modules/network/f5/test_bigip_config.py +++ b/test/units/modules/network/f5/test_bigip_config.py @@ -14,25 +14,30 @@ from nose.plugins.skip import SkipTest if sys.version_info < (2, 7): raise SkipTest("F5 Ansible modules require Python >= 2.7") -from units.compat import unittest -from units.compat.mock import Mock -from units.compat.mock import patch from ansible.module_utils.basic import AnsibleModule try: from library.modules.bigip_config import Parameters from library.modules.bigip_config import ModuleManager from library.modules.bigip_config import ArgumentSpec - from library.module_utils.network.f5.common import F5ModuleError - from library.module_utils.network.f5.common import iControlUnexpectedHTTPError - from test.unit.modules.utils import set_module_args + + # In Ansible 2.8, Ansible changed import paths. + from test.units.compat import unittest + from test.units.compat.mock import Mock + from test.units.compat.mock import patch + + from test.units.modules.utils import set_module_args except ImportError: try: from ansible.modules.network.f5.bigip_config import Parameters from ansible.modules.network.f5.bigip_config import ModuleManager from ansible.modules.network.f5.bigip_config import ArgumentSpec - from ansible.module_utils.network.f5.common import F5ModuleError - from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError + + # Ansible 2.8 imports + from units.compat import unittest + from units.compat.mock import Mock + from units.compat.mock import patch + from units.modules.utils import set_module_args except ImportError: raise SkipTest("F5 Ansible modules require the f5-sdk Python library")