From f6309d19a398cc51225ad1feac34c8596f80baa3 Mon Sep 17 00:00:00 2001 From: Tim Rupp Date: Fri, 2 Nov 2018 11:54:40 -0700 Subject: [PATCH] Removes f5-sdk from bigip_uce_fetch module (#48002) This is part of the ongoing effort to fully remove the f5-sdk from the f5 ansible modules --- .../modules/network/f5/bigip_ucs_fetch.py | 201 +++++++++++++----- .../network/f5/test_bigip_ucs_fetch.py | 21 +- 2 files changed, 157 insertions(+), 65 deletions(-) diff --git a/lib/ansible/modules/network/f5/bigip_ucs_fetch.py b/lib/ansible/modules/network/f5/bigip_ucs_fetch.py index 167e79bb43..8ac737de9d 100644 --- a/lib/ansible/modules/network/f5/bigip_ucs_fetch.py +++ b/lib/ansible/modules/network/f5/bigip_ucs_fetch.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 @@ -65,6 +65,7 @@ notes: extends_documentation_fragment: f5 author: - Tim Rupp (@caphrim007) + - Wojciech Wypior (@wojtek0806) ''' EXAMPLES = r''' @@ -146,32 +147,37 @@ from ansible.module_utils.basic import AnsibleModule from distutils.version import LooseVersion 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 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.common import transform_name + from library.module_utils.network.f5.icontrol import download_file + from library.module_utils.network.f5.icontrol import tmos_version 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 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 exit_json + from ansible.module_utils.network.f5.common import fail_json + from ansible.module_utils.network.f5.common import transform_name + from ansible.module_utils.network.f5.icontrol import download_file + from ansible.module_utils.network.f5.icontrol import tmos_version class Parameters(AnsibleF5Parameters): updatables = [] - returnables = ['dest', 'src', 'md5sum', 'checksum', 'backup_file'] + returnables = [ + 'dest', + 'src', + 'md5sum', + 'checksum', + 'backup_file'] api_attributes = [] api_map = {} @@ -271,7 +277,7 @@ class ModuleManager(object): :return: bool """ - version = self.client.api.tmos_version + version = tmos_version(self.client) if LooseVersion(version) < LooseVersion('12.1.0'): return True else: @@ -288,10 +294,7 @@ class BaseManager(object): def exec_module(self): result = dict() - try: - self.present() - except iControlUnexpectedHTTPError as e: - raise F5ModuleError(str(e)) + self.present() reportable = ReportableChanges(params=self.changes.to_return()) changes = reportable.to_return() @@ -366,19 +369,34 @@ class BaseManager(object): def create_on_device(self): if self.want.passphrase: - self.client.api.tm.sys.ucs.exec_cmd( - 'save', + params = dict( + command='save', name=self.want.src, options=[{'passphrase': self.want.encryption_password}] ) else: - self.client.api.tm.sys.ucs.exec_cmd( - 'save', - name=self.want.src + params = dict( + command='save', + name=self.want.src, ) + uri = "https://{0}:{1}/mgmt/tm/sys/ucs".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)) + if 'code' in response and response['code'] in [400, 403]: + if 'message' in response: + raise F5ModuleError(response['message']) + else: + raise F5ModuleError(resp.content) + def download(self): - self.download_from_device() + self.download_from_device(self.want.dest) if os.path.exists(self.want.dest): return True raise F5ModuleError( @@ -394,16 +412,33 @@ class V1Manager(BaseManager): def read_current(self): result = None output = self.read_current_from_device() - if hasattr(output, 'commandResult'): - result = self._read_ucs_files_from_output(output.commandResult) + if 'commandResult' in output: + result = self._read_ucs_files_from_output(output['commandResult']) return result def read_current_from_device(self): - output = self.client.api.tm.util.bash.exec_cmd( - 'run', + params = dict( + command='run', utilCmdArgs='-c "tmsh list sys ucs"' ) - return output + + uri = "https://{0}:{1}/mgmt/tm/util/bash".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)) + if 'code' in response and response['code'] in [400, 403]: + if 'message' in response: + raise F5ModuleError(response['message']) + else: + raise F5ModuleError(resp.content) + return response def _read_ucs_files_from_output(self, output): search = re.compile(r'filename\s+(.*)').search @@ -418,40 +453,84 @@ class V1Manager(BaseManager): return True return False - def download_from_device(self): - madm = self.client.api.shared.file_transfer.madm - madm.download_file(self.want.filename, self.want.dest) + def download_from_device(self, dest): + url = 'https://{0}:{1}/mgmt/shared/file-transfer/madm/{2}'.format( + self.client.provider['server'], + self.client.provider['server_port'], + self.want.filename + ) + try: + download_file(self.client, url, dest) + except F5ModuleError: + raise F5ModuleError( + "Failed to download the file." + ) if os.path.exists(self.want.dest): return True return False def _move_to_download(self): + move_path = '/var/local/ucs/{0} {1}/{0}'.format( + self.want.filename, self.remote_dir + ) + params = dict( + command='run', + utilCmdArgs=move_path + ) + + uri = "https://{0}:{1}/mgmt/tm/util/unix-mv/".format( + self.client.provider['server'], + self.client.provider['server_port'] + ) + + resp = self.client.api.post(uri, json=params) + try: - move_path = '/var/local/ucs/{0} {1}/{0}'.format( - self.want.filename, self.remote_dir - ) - self.client.api.tm.util.unix_mv.exec_cmd( - 'run', - utilCmdArgs=move_path - ) - return True - except Exception: - return False + response = resp.json() + if 'commandResult' in response: + if 'cannot stat' in response['commandResult']: + raise F5ModuleError(response['commandResult']) + 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) + + return True class V2Manager(BaseManager): + def read_current_from_device(self): + uri = "https://{0}:{1}/mgmt/tm/sys/ucs".format( + self.client.provider['server'], + self.client.provider['server_port'], + ) + 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 response + def read_current(self): collection = self.read_current_from_device() - if 'items' not in collection.attrs: + if 'items' not in collection: return [] - resources = collection.attrs['items'] + resources = collection['items'] result = [x['apiRawValues']['filename'] for x in resources] return result - def read_current_from_device(self): - collection = self.client.api.tm.sys.ucs.load() - return collection - def exists(self): collection = self.read_current() base = os.path.basename(self.want.src) @@ -459,9 +538,18 @@ class V2Manager(BaseManager): return True return False - def download_from_device(self): - ucs = self.client.api.shared.file_transfer.ucs_downloads - ucs.download_file(self.want.src, self.want.dest) + def download_from_device(self, dest): + url = 'https://{0}:{1}/mgmt/shared/file-transfer/ucs-downloads/{2}'.format( + self.client.provider['server'], + self.client.provider['server_port'], + self.want.src + ) + try: + download_file(self.client, url, dest) + except F5ModuleError: + raise F5ModuleError( + "Failed to download the file." + ) if os.path.exists(self.want.dest): return True return False @@ -508,18 +596,17 @@ def main(): supports_check_mode=spec.supports_check_mode, add_file_common_args=spec.add_file_common_args ) - 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/test/units/modules/network/f5/test_bigip_ucs_fetch.py b/test/units/modules/network/f5/test_bigip_ucs_fetch.py index 2ed9e8c337..15cbe408c5 100644 --- a/test/units/modules/network/f5/test_bigip_ucs_fetch.py +++ b/test/units/modules/network/f5/test_bigip_ucs_fetch.py @@ -14,9 +14,6 @@ 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: @@ -25,9 +22,13 @@ try: from library.modules.bigip_ucs_fetch import V1Manager from library.modules.bigip_ucs_fetch import V2Manager from library.modules.bigip_ucs_fetch 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_ucs_fetch import Parameters @@ -35,8 +36,12 @@ except ImportError: from ansible.modules.network.f5.bigip_ucs_fetch import V1Manager from ansible.modules.network.f5.bigip_ucs_fetch import V2Manager from ansible.modules.network.f5.bigip_ucs_fetch 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")