diff --git a/lib/ansible/modules/network/f5/bigip_sys_global.py b/lib/ansible/modules/network/f5/bigip_sys_global.py index 15c576dcdb1..2219842a068 100644 --- a/lib/ansible/modules/network/f5/bigip_sys_global.py +++ b/lib/ansible/modules/network/f5/bigip_sys_global.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 @@ -29,12 +29,12 @@ options: off a user that is logged on. gui_setup: description: - - C(enable) or C(disabled) the Setup utility in the browser-based + - C(yes) or C(no) the Setup utility in the browser-based Configuration utility. type: bool lcd_display: description: - - Specifies, when C(enabled), that the system menu displays on the + - Specifies, when C(yes), that the system menu displays on the LCD screen on the front of the unit. This setting has no effect when used on the VE platform. type: bool @@ -45,14 +45,14 @@ options: type: bool net_reboot: description: - - Specifies, when C(enabled), that the next time you reboot the system, + - Specifies, when C(yes), that the next time you reboot the system, the system boots to an ISO image on the network, rather than an internal media drive. type: bool quiet_boot: description: - - Specifies, when C(enabled), that the system suppresses informational - text on the console during the boot cycle. When C(disabled), the + - Specifies, when C(yes), that the system suppresses informational + text on the console during the boot cycle. When C(no), the system presents messages and informational text on the console during the boot cycle. type: bool @@ -69,20 +69,19 @@ options: choices: - present extends_documentation_fragment: f5 -requirements: - - f5-sdk author: - Tim Rupp (@caphrim007) + - Wojciech Wypior (@wojtek0806) ''' EXAMPLES = r''' - name: Disable the setup utility bigip_sys_global: gui_setup: no - password: secret - server: lb.mydomain.com - user: admin - state: present + provider: + password: secret + server: lb.mydomain.com + user: admin delegate_to: localhost ''' @@ -102,67 +101,59 @@ console_timeout: gui_setup: description: The new setting for the Setup utility. returned: changed - type: string + type: bool sample: yes lcd_display: description: The new setting for displaying the system menu on the LCD. returned: changed - type: string + type: bool sample: yes mgmt_dhcp: description: The new setting for whether the mgmt interface should DHCP or not. returned: changed - type: string + type: bool sample: yes net_reboot: description: The new setting for whether the system should boot to an ISO on the network or not. returned: changed - type: string + type: bool sample: yes quiet_boot: description: - The new setting for whether the system should suppress information to the console during boot or not. returned: changed - type: string + type: bool sample: yes security_banner: description: - The new setting for whether the system should display an advisory message on the login screen or not. returned: changed - type: string + type: bool sample: yes ''' from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE -from ansible.module_utils.parsing.convert_bool import BOOLEANS_FALSE 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 flatten_boolean 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 flatten_boolean class Parameters(AnsibleF5Parameters): @@ -174,80 +165,73 @@ class Parameters(AnsibleF5Parameters): 'mgmtDhcp': 'mgmt_dhcp', 'netReboot': 'net_reboot', 'quietBoot': 'quiet_boot', - 'consoleInactivityTimeout': 'console_timeout' + 'consoleInactivityTimeout': 'console_timeout', } api_attributes = [ - 'guiSecurityBanner', 'guiSecurityBannerText', 'guiSetup', 'lcdDisplay', - 'mgmtDhcp', 'netReboot', 'quietBoot', 'consoleInactivityTimeout' + 'guiSecurityBanner', + 'guiSecurityBannerText', + 'guiSetup', + 'lcdDisplay', + 'mgmtDhcp', + 'netReboot', + 'quietBoot', + 'consoleInactivityTimeout', ] returnables = [ - 'security_banner', 'banner_text', 'gui_setup', 'lcd_display', - 'mgmt_dhcp', 'net_reboot', 'quiet_boot', 'console_timeout' + 'security_banner', + 'banner_text', + 'gui_setup', + 'lcd_display', + 'mgmt_dhcp', + 'net_reboot', + 'quiet_boot', + 'console_timeout', ] updatables = [ - 'security_banner', 'banner_text', 'gui_setup', 'lcd_display', - 'mgmt_dhcp', 'net_reboot', 'quiet_boot', 'console_timeout' + 'security_banner', + 'banner_text', + 'gui_setup', + 'lcd_display', + 'mgmt_dhcp', + 'net_reboot', + 'quiet_boot', + 'console_timeout', ] + @property + def security_banner(self): + return flatten_boolean(self._values['security_banner']) + + @property + def gui_setup(self): + return flatten_boolean(self._values['gui_setup']) + + @property + def lcd_display(self): + return flatten_boolean(self._values['lcd_display']) + + @property + def mgmt_dhcp(self): + return flatten_boolean(self._values['mgmt_dhcp']) + + @property + def net_reboot(self): + return flatten_boolean(self._values['net_reboot']) + + @property + def quiet_boot(self): + return flatten_boolean(self._values['quiet_boot']) + class ApiParameters(Parameters): pass class ModuleParameters(Parameters): - def _get_boolean_like_return_value(self, parameter): - if self._values[parameter] is None: - return None - elif self._values[parameter] in ['enabled', 'disabled']: - self._values['__warnings'].append( - dict(version='2.5', msg='enabled/disabled are deprecated. Use boolean values (true, yes, no, 1, 0) instead.') - ) - true = list(BOOLEANS_TRUE) + ['True'] - false = list(BOOLEANS_FALSE) + ['False'] - if self._values[parameter] in true: - return 'enabled' - if self._values[parameter] in false: - return 'disabled' - else: - return str(self._values[parameter]) - - @property - def security_banner(self): - result = self._get_boolean_like_return_value('security_banner') - return result - - @property - def gui_setup(self): - result = self._get_boolean_like_return_value('gui_setup') - return result - - @property - def banner_text(self): - result = self._get_boolean_like_return_value('banner_text') - return result - - @property - def lcd_display(self): - result = self._get_boolean_like_return_value('lcd_display') - return result - - @property - def mgmt_dhcp(self): - result = self._get_boolean_like_return_value('mgmt_dhcp') - return result - - @property - def net_reboot(self): - result = self._get_boolean_like_return_value('net_reboot') - return result - - @property - def quiet_boot(self): - result = self._get_boolean_like_return_value('quiet_boot') - return result + pass class Changes(Parameters): @@ -263,11 +247,79 @@ class Changes(Parameters): class UsableChanges(Changes): - pass + @property + def security_banner(self): + if self._values['security_banner'] is None: + return None + if self._values['security_banner'] == 'yes': + return 'enabled' + return 'disabled' + + @property + def gui_setup(self): + if self._values['gui_setup'] is None: + return None + if self._values['gui_setup'] == 'yes': + return 'enabled' + return 'disabled' + + @property + def lcd_display(self): + if self._values['lcd_display'] is None: + return None + if self._values['lcd_display'] == 'yes': + return 'enabled' + return 'disabled' + + @property + def mgmt_dhcp(self): + if self._values['mgmt_dhcp'] is None: + return None + if self._values['mgmt_dhcp'] == 'yes': + return 'enabled' + return 'disabled' + + @property + def net_reboot(self): + if self._values['net_reboot'] is None: + return None + if self._values['net_reboot'] == 'yes': + return 'enabled' + return 'disabled' + + @property + def quiet_boot(self): + if self._values['quiet_boot'] is None: + return None + if self._values['quiet_boot'] == 'yes': + return 'enabled' + return 'disabled' class ReportableChanges(Changes): - pass + @property + def security_banner(self): + return flatten_boolean(self._values['security_banner']) + + @property + def gui_setup(self): + return flatten_boolean(self._values['gui_setup']) + + @property + def lcd_display(self): + return flatten_boolean(self._values['lcd_display']) + + @property + def mgmt_dhcp(self): + return flatten_boolean(self._values['mgmt_dhcp']) + + @property + def net_reboot(self): + return flatten_boolean(self._values['net_reboot']) + + @property + def quiet_boot(self): + return flatten_boolean(self._values['quiet_boot']) class Difference(object): @@ -283,13 +335,13 @@ class Difference(object): return self.__default(param) def __default(self, param): - attr1 = getattr(self.want, param) + want = getattr(self.want, param) try: - attr2 = getattr(self.have, param) - if attr1 != attr2: - return attr1 + have = getattr(self.have, param) + if want != have: + return want except AttributeError: - return attr1 + return want class ModuleManager(object): @@ -335,10 +387,7 @@ class ModuleManager(object): def exec_module(self): result = dict() - try: - changed = self.present() - except iControlUnexpectedHTTPError as e: - raise F5ModuleError(str(e)) + changed = self.present() reportable = ReportableChanges(params=self.changes.to_return()) changes = reportable.to_return() @@ -358,11 +407,6 @@ class ModuleManager(object): def present(self): return self.update() - def read_current_from_device(self): - resource = self.client.api.tm.sys.global_settings.load() - result = resource.attrs - return ApiParameters(params=result) - def update(self): self.have = self.read_current_from_device() if not self.should_update(): @@ -372,10 +416,41 @@ class ModuleManager(object): self.update_on_device() return True + def read_current_from_device(self): + uri = "https://{0}:{1}/mgmt/tm/sys/global-settings/".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 ApiParameters(params=response) + def update_on_device(self): - params = self.want.api_params() - resource = self.client.api.tm.sys.global_settings.load() - resource.modify(**params) + params = self.changes.api_params() + uri = "https://{0}:{1}/mgmt/tm/sys/global-settings/".format( + self.client.provider['server'], + self.client.provider['server_port'], + ) + 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) class ArgumentSpec(object): @@ -402,8 +477,12 @@ class ArgumentSpec(object): quiet_boot=dict( type='bool' ), - console_timeout=dict(required=False, type='int', default=None), - state=dict(default='present', choices=['present']) + console_timeout=dict( + type='int' + ), + state=dict( + default='present', choices=['present'] + ) ) self.argument_spec = {} self.argument_spec.update(f5_argument_spec) @@ -417,18 +496,17 @@ 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) - 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_sys_global.py b/test/units/modules/network/f5/test_bigip_sys_global.py index c99b81995ae..6c5bc9377f3 100644 --- a/test/units/modules/network/f5/test_bigip_sys_global.py +++ b/test/units/modules/network/f5/test_bigip_sys_global.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: @@ -24,17 +21,25 @@ try: from library.modules.bigip_sys_global import ModuleParameters from library.modules.bigip_sys_global import ModuleManager from library.modules.bigip_sys_global 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_sys_global import ApiParameters from ansible.modules.network.f5.bigip_sys_global import ModuleParameters from ansible.modules.network.f5.bigip_sys_global import ModuleManager from ansible.modules.network.f5.bigip_sys_global 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") @@ -76,24 +81,24 @@ class TestParameters(unittest.TestCase): p = ModuleParameters(params=args) assert p.banner_text == 'this is a banner' assert p.console_timeout == 100 - assert p.gui_setup == 'enabled' - assert p.lcd_display == 'enabled' - assert p.mgmt_dhcp == 'enabled' - assert p.net_reboot == 'enabled' - assert p.quiet_boot == 'enabled' - assert p.security_banner == 'enabled' + assert p.gui_setup == 'yes' + assert p.lcd_display == 'yes' + assert p.mgmt_dhcp == 'yes' + assert p.net_reboot == 'yes' + assert p.quiet_boot == 'yes' + assert p.security_banner == 'yes' def test_api_parameters(self): args = load_fixture('load_sys_global_settings.json') p = ApiParameters(params=args) assert 'Welcome to the BIG-IP Configuration Utility' in p.banner_text assert p.console_timeout == 0 - assert p.gui_setup == 'disabled' - assert p.lcd_display == 'enabled' - assert p.mgmt_dhcp == 'enabled' - assert p.net_reboot == 'disabled' - assert p.quiet_boot == 'enabled' - assert p.security_banner == 'enabled' + assert p.gui_setup == 'no' + assert p.lcd_display == 'yes' + assert p.mgmt_dhcp == 'yes' + assert p.net_reboot == 'no' + assert p.quiet_boot == 'yes' + assert p.security_banner == 'yes' class TestManager(unittest.TestCase):