From ccb6349e703691bbf46914dbee4a1fee219ac853 Mon Sep 17 00:00:00 2001 From: Tim Rupp Date: Sat, 10 Nov 2018 12:06:32 -0800 Subject: [PATCH] Removes f5-sdk from bigip_device_license (#48476) --- .../network/f5/bigip_device_license.py | 297 ++++++++++++++---- .../network/f5/test_bigip_device_license.py | 21 +- 2 files changed, 241 insertions(+), 77 deletions(-) diff --git a/lib/ansible/modules/network/f5/bigip_device_license.py b/lib/ansible/modules/network/f5/bigip_device_license.py index f4865e3099..0b04c30102 100644 --- a/lib/ansible/modules/network/f5/bigip_device_license.py +++ b/lib/ansible/modules/network/f5/bigip_device_license.py @@ -1,7 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- # -# Copyright (c) 2016 F5 Networks Inc. +# Copyright: (c) 2016, 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 @@ -90,31 +90,25 @@ import time import xml.etree.ElementTree 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 exit_json + from library.module_utils.network.f5.common import fail_json from library.module_utils.network.f5.icontrol import iControlRestSession - try: - from library.module_utils.network.f5.common import iControlUnexpectedHTTPError - from f5.sdk_exception import UtilError - 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 exit_json + from ansible.module_utils.network.f5.common import fail_json from ansible.module_utils.network.f5.icontrol import iControlRestSession - try: - from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError - from f5.sdk_exception import UtilError - except ImportError: - HAS_F5SDK = False class LicenseXmlParser(object): @@ -176,10 +170,17 @@ class LicenseXmlParser(object): def get_fault(self): result = dict() + self.set_result_for_license_fault(result) + self.set_result_for_general_fault(result) + + if 'faultNumber' not in result: + result['faultNumber'] = None + return result + + def set_result_for_license_fault(self, result): root = self.find_element('LicensingFault') if root is None: return result - for elem in root: if elem.tag == 'faultNumber': result['faultNumber'] = int(elem.text) @@ -189,9 +190,17 @@ class LicenseXmlParser(object): result['faultText'] = None else: result['faultText'] = elem.text - if 'faultNumber' not in result: - result['faultNumber'] = None - return result + + def set_result_for_general_fault(self, result): + namespaces = { + 'ns2': 'http://schemas.xmlsoap.org/soap/envelope/' + } + root = self.content.findall('.//ns2:Fault', namespaces) + if len(root) == 0: + return None + for elem in root[0]: + if elem.tag == 'faultstring': + result['faultText'] = elem.text def json(self): result = dict( @@ -206,7 +215,7 @@ class LicenseXmlParser(object): class Parameters(AnsibleF5Parameters): api_map = { - 'licenseEndDateTime': 'license_end_date_time' + 'licenseEndDateTime': 'license_end_date_time', } api_attributes = [ @@ -372,13 +381,10 @@ class ModuleManager(object): 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)) + if state == "present": + changed = self.present() + elif state == "absent": + changed = self.absent() reportable = ReportableChanges(params=self.changes.to_return()) changes = reportable.to_return() @@ -410,25 +416,45 @@ class ModuleManager(object): return True def read_dossier_from_device(self): - result = self.client.api.tm.util.get_dossier.exec_cmd( - 'run', utilCmdArgs='-b {0}'.format(self.want.license_key) + params = dict( + command='run', + utilCmdArgs='-b "{0}"'.format(self.want.license_key) ) + uri = "https://{0}:{1}/mgmt/tm/util/get-dossier".format( + self.client.provider['server'], + self.client.provider['server_port'] + ) + resp = self.client.api.post(uri, json=params) try: - return result.commandResult + 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) + try: + return response['commandResult'] except Exception: return None def generate_license_from_remote(self): - mgmt = iControlRestSession() - mgmt.verify = False - mgmt.headers = { - 'SOAPAction': '""', - 'Content-Type': 'text/xml; charset=utf-8', - } + mgmt = iControlRestSession( + validate_certs=False, + headers={ + 'SOAPAction': '""', + 'Content-Type': 'text/xml; charset=utf-8', + } + ) for x in range(0, 10): try: - resp = mgmt.post(self.want.license_url, data=self.want.license_envelope) + resp = mgmt.post( + self.want.license_url, + data=self.want.license_envelope, + ) except Exception as ex: continue @@ -493,9 +519,24 @@ class ModuleManager(object): return False def exists(self): - resource = self.client.api.tm.shared.licensing.registration.load() + uri = "https://{0}:{1}/mgmt/tm/shared/licensing/registration".format( + self.client.provider['server'], + self.client.provider['server_port'], + ) + resp = self.client.api.get(uri) try: - if resource.registrationKey == self.want.license_key: + 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) + + try: + if response['registrationKey'] == self.want.license_key: return True except Exception: pass @@ -519,20 +560,51 @@ class ModuleManager(object): def _is_mcpd_ready_on_device(self): try: - output = self.client.api.tm.util.bash.exec_cmd( - 'run', - utilCmdArgs='-c "tmsh show sys mcp-state | grep running"' + command = "tmsh show sys mcp-state | grep running" + params = dict( + command='run', + utilCmdArgs='-c "{0}"'.format(command) ) - if hasattr(output, 'commandResult'): + 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) + + if 'commandResult' in response: return True except Exception as ex: pass return False def any_license_exists(self): - resource = self.client.api.tm.shared.licensing.registration.load() + uri = "https://{0}:{1}/mgmt/tm/shared/licensing/registration".format( + self.client.provider['server'], + self.client.provider['server_port'], + ) + resp = self.client.api.get(uri) try: - if resource.registrationKey is not None: + 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) + try: + if response['registrationKey'] is not None: return True except Exception: pass @@ -562,21 +634,75 @@ class ModuleManager(object): def upload_license_to_device(self, license): license_payload = re.sub(self.escape_patterns, r'\\\1', license['license']) - command = """cat > /config/bigip.license < /config/bigip.license < /LICENSE.F5 < /LICENSE.F5 <= 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: @@ -24,17 +21,25 @@ try: from library.modules.bigip_device_license import ModuleParameters from library.modules.bigip_device_license import ModuleManager from library.modules.bigip_device_license 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_device_license import ApiParameters from ansible.modules.network.f5.bigip_device_license import ModuleParameters from ansible.modules.network.f5.bigip_device_license import ModuleManager from ansible.modules.network.f5.bigip_device_license 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")