From 4d691e9d7b5386b29345ca45168d9b4c6caa5d34 Mon Sep 17 00:00:00 2001 From: Tim Rupp Date: Thu, 10 May 2018 10:59:49 -0700 Subject: [PATCH] Various fixes and features for bigip modules (#39972) * Removed forwarders parameter that did not work * Updated coding conventions * Added ssl_cipher_suite and ssl_protocols to bigip_device_httpd * Added more unit tests --- .../modules/network/f5/bigip_device_dns.py | 29 +-- .../modules/network/f5/bigip_device_group.py | 9 +- .../network/f5/bigip_device_group_member.py | 9 +- .../modules/network/f5/bigip_device_httpd.py | 179 ++++++++++++++++-- .../modules/network/f5/bigip_device_ntp.py | 9 +- .../modules/network/f5/bigip_device_sshd.py | 39 ++-- .../modules/network/f5/bigip_device_trust.py | 11 +- .../network/f5/bigip_gtm_datacenter.py | 9 +- .../fixtures/load_sys_httpd_non_default.json | 36 ++++ .../network/f5/test_bigip_device_dns.py | 17 +- .../network/f5/test_bigip_device_group.py | 6 +- .../f5/test_bigip_device_group_member.py | 6 +- .../network/f5/test_bigip_device_httpd.py | 168 +++++++++++++++- .../network/f5/test_bigip_device_ntp.py | 6 +- .../network/f5/test_bigip_device_sshd.py | 16 +- .../network/f5/test_bigip_device_trust.py | 26 ++- .../network/f5/test_bigip_gtm_datacenter.py | 8 +- 17 files changed, 445 insertions(+), 138 deletions(-) create mode 100644 test/units/modules/network/f5/fixtures/load_sys_httpd_non_default.json diff --git a/lib/ansible/modules/network/f5/bigip_device_dns.py b/lib/ansible/modules/network/f5/bigip_device_dns.py index 9e5a9f69c0c..42aef16b2ab 100644 --- a/lib/ansible/modules/network/f5/bigip_device_dns.py +++ b/lib/ansible/modules/network/f5/bigip_device_dns.py @@ -18,7 +18,7 @@ module: bigip_device_dns short_description: Manage BIG-IP device DNS settings description: - Manage BIG-IP device DNS settings -version_added: "2.2" +version_added: 2.2 options: cache: description: @@ -33,10 +33,6 @@ options: name_servers: description: - A list of name servers that the system uses to validate DNS lookups - forwarders: - description: - - A list of BIND servers that the system can use to perform DNS lookups - - Deprecated in 2.4. Use the GUI or edit named.conf. search: description: - A list of domains that the system searches for local domain lookups, @@ -106,30 +102,23 @@ warnings: from ansible.module_utils.basic import AnsibleModule -HAS_DEVEL_IMPORTS = False - try: - # Sideband repository used for dev 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.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 fqdn_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 - HAS_DEVEL_IMPORTS = True except ImportError: - # Upstream Ansible 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.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 fqdn_name from ansible.module_utils.network.f5.common import f5_argument_spec try: from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError @@ -198,15 +187,6 @@ class Parameters(AnsibleF5Parameters): valid = ['enable', 'enabled'] return True if self._values['dhcp'] in valid else False - @property - def forwarders(self): - if self._values['forwarders'] is None: - return None - else: - raise F5ModuleError( - "The modifying of forwarders is not supported." - ) - @property def ip_version(self): if self._values['ip_version'] in [6, '6', 'options inet6']: @@ -344,11 +324,6 @@ class ArgumentSpec(object): default=None, type='list' ), - forwarders=dict( - required=False, - default=None, - type='list' - ), search=dict( required=False, default=None, @@ -369,7 +344,7 @@ class ArgumentSpec(object): self.argument_spec.update(f5_argument_spec) self.argument_spec.update(argument_spec) self.required_one_of = [ - ['name_servers', 'search', 'forwarders', 'ip_version', 'cache'] + ['name_servers', 'search', 'ip_version', 'cache'] ] diff --git a/lib/ansible/modules/network/f5/bigip_device_group.py b/lib/ansible/modules/network/f5/bigip_device_group.py index d270a6202ed..7bd9e5dfc9d 100644 --- a/lib/ansible/modules/network/f5/bigip_device_group.py +++ b/lib/ansible/modules/network/f5/bigip_device_group.py @@ -21,7 +21,7 @@ description: of BIG-IP devices. Usage of this module should be done in conjunction with the C(bigip_configsync_actions) to sync configuration across the pair or cluster if auto-sync is disabled. -version_added: "2.5" +version_added: 2.5 options: name: description: @@ -149,30 +149,23 @@ max_incremental_sync_size: from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.parsing.convert_bool import BOOLEANS_TRUE -HAS_DEVEL_IMPORTS = False - try: - # Sideband repository used for dev 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.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 fqdn_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 - HAS_DEVEL_IMPORTS = True except ImportError: - # Upstream Ansible 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.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 fqdn_name from ansible.module_utils.network.f5.common import f5_argument_spec try: from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError diff --git a/lib/ansible/modules/network/f5/bigip_device_group_member.py b/lib/ansible/modules/network/f5/bigip_device_group_member.py index b6a1bf912d6..dbd6af57b6b 100644 --- a/lib/ansible/modules/network/f5/bigip_device_group_member.py +++ b/lib/ansible/modules/network/f5/bigip_device_group_member.py @@ -20,7 +20,7 @@ description: be added or removed, never updated. This is because the members are identified by unique name values and changing that name would invalidate the uniqueness. -version_added: "2.5" +version_added: 2.5 options: name: description: @@ -77,30 +77,23 @@ RETURN = r''' from ansible.module_utils.basic import AnsibleModule -HAS_DEVEL_IMPORTS = False - try: - # Sideband repository used for dev 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.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 fqdn_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 - HAS_DEVEL_IMPORTS = True except ImportError: - # Upstream Ansible 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.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 fqdn_name from ansible.module_utils.network.f5.common import f5_argument_spec try: from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError diff --git a/lib/ansible/modules/network/f5/bigip_device_httpd.py b/lib/ansible/modules/network/f5/bigip_device_httpd.py index 7c5802e5700..c87a0fe3fa6 100644 --- a/lib/ansible/modules/network/f5/bigip_device_httpd.py +++ b/lib/ansible/modules/network/f5/bigip_device_httpd.py @@ -19,7 +19,7 @@ short_description: Manage HTTPD related settings on BIG-IP description: - Manages HTTPD related settings on the BIG-IP. These settings are interesting to change when you want to set GUI timeouts and other TMUI related settings. -version_added: "2.5" +version_added: 2.5 options: allow: description: @@ -64,6 +64,31 @@ options: ssl_port: description: - The HTTPS port to listen on. + ssl_cipher_suite: + description: + - Specifies the ciphers that the system uses. + - The values in the suite are separated by colons (:). + - Can be specified in either a string or list form. The list form is the + recommended way to provide the cipher suite. See examples for usage. + - Use the value C(default) to set the cipher suite to the system default. + This value is equivalent to specifying a list of C(ECDHE-RSA-AES128-GCM-SHA256, + ECDHE-RSA-AES256-GCM-SHA384,ECDHE-RSA-AES128-SHA,ECDHE-RSA-AES256-SHA, + ECDHE-RSA-AES128-SHA256,ECDHE-RSA-AES256-SHA384,ECDHE-ECDSA-AES128-GCM-SHA256, + ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-ECDSA-AES128-SHA,ECDHE-ECDSA-AES256-SHA, + ECDHE-ECDSA-AES128-SHA256,ECDHE-ECDSA-AES256-SHA384,AES128-GCM-SHA256, + AES256-GCM-SHA384,AES128-SHA,AES256-SHA,AES128-SHA256,AES256-SHA256, + ECDHE-RSA-DES-CBC3-SHA,ECDHE-ECDSA-DES-CBC3-SHA,DES-CBC3-SHA). + version_added: 2.6 + ssl_protocols: + description: + - The list of SSL protocols to accept on the management console. + - A space-separated list of tokens in the format accepted by the Apache + mod_ssl SSLProtocol directive. + - Can be specified in either a string or list form. The list form is the + recommended way to provide the cipher suite. See examples for usage. + - Use the value C(default) to set the SSL protocols to the system default. + This value is equivalent to specifying a list of C(all,-SSLv2,-SSLv3). + version_added: 2.6 notes: - Requires the requests Python package on the host. This is as easy as C(pip install requests). @@ -99,6 +124,45 @@ EXAMPLES = r''' server: lb.mydomain.com user: admin delegate_to: localhost + +- name: Set SSL cipher suite by list + bigip_device_httpd: + password: secret + server: lb.mydomain.com + user: admin + ssl_cipher_suite: + - ECDHE-RSA-AES128-GCM-SHA256 + - ECDHE-RSA-AES256-GCM-SHA384 + - ECDHE-RSA-AES128-SHA + - AES256-SHA256 + delegate_to: localhost + +- name: Set SSL cipher suite by string + bigip_device_httpd: + password: secret + server: lb.mydomain.com + user: admin + ssl_cipher_suite: ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA:AES256-SHA256 + delegate_to: localhost + +- name: Set SSL protocols by list + bigip_device_httpd: + password: secret + server: lb.mydomain.com + user: admin + ssl_protocols: + - all + - -SSLv2 + - -SSLv3 + delegate_to: localhost + +- name: Set SSL protocols by string + bigip_device_httpd: + password: secret + server: lb.mydomain.com + user: admin + ssl_cipher_suite: all -SSLv2 -SSLv3 + delegate_to: localhost ''' RETURN = r''' @@ -152,36 +216,40 @@ ssl_port: returned: changed type: int sample: 10443 +ssl_cipher_suite: + description: The new ciphers that the system uses. + returned: changed + type: string + sample: ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA +ssl_protocols: + description: The new list of SSL protocols to accept on the management console. + returned: changed + type: string + sample: all -SSLv2 -SSLv3 ''' import time from ansible.module_utils.basic import AnsibleModule - -HAS_DEVEL_IMPORTS = False +from ansible.module_utils.six import string_types try: - # Sideband repository used for dev 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.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 fqdn_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 - HAS_DEVEL_IMPORTS = True except ImportError: - # Upstream Ansible 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.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 fqdn_name from ansible.module_utils.network.f5.common import f5_argument_spec try: from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError @@ -206,29 +274,55 @@ class Parameters(AnsibleF5Parameters): 'logLevel': 'log_level', 'maxClients': 'max_clients', 'redirectHttpToHttps': 'redirect_http_to_https', - 'sslPort': 'ssl_port' + 'sslPort': 'ssl_port', + 'sslCiphersuite': 'ssl_cipher_suite', + 'sslProtocol': 'ssl_protocols' } api_attributes = [ 'authPamIdleTimeout', 'authPamValidateIp', 'authName', 'authPamDashboardTimeout', 'fastcgiTimeout', 'hostnameLookup', 'logLevel', 'maxClients', 'sslPort', - 'redirectHttpToHttps', 'allow' + 'redirectHttpToHttps', 'allow', 'sslCiphersuite', 'sslProtocol' ] returnables = [ 'auth_pam_idle_timeout', 'auth_pam_validate_ip', 'auth_name', 'auth_pam_dashboard_timeout', 'fast_cgi_timeout', 'hostname_lookup', 'log_level', 'max_clients', 'redirect_http_to_https', 'ssl_port', - 'allow' + 'allow', 'ssl_cipher_suite', 'ssl_protocols' ] updatables = [ 'auth_pam_idle_timeout', 'auth_pam_validate_ip', 'auth_name', 'auth_pam_dashboard_timeout', 'fast_cgi_timeout', 'hostname_lookup', 'log_level', 'max_clients', 'redirect_http_to_https', 'ssl_port', - 'allow' + 'allow', 'ssl_cipher_suite', 'ssl_protocols' ] + _ciphers = "ECDHE-RSA-AES128-GCM-SHA256:" \ + "ECDHE-RSA-AES256-GCM-SHA384:" \ + "ECDHE-RSA-AES128-SHA:" \ + "ECDHE-RSA-AES256-SHA:" \ + "ECDHE-RSA-AES128-SHA256:" \ + "ECDHE-RSA-AES256-SHA384:" \ + "ECDHE-ECDSA-AES128-GCM-SHA256:" \ + "ECDHE-ECDSA-AES256-GCM-SHA384:" \ + "ECDHE-ECDSA-AES128-SHA:" \ + "ECDHE-ECDSA-AES256-SHA:" \ + "ECDHE-ECDSA-AES128-SHA256:" \ + "ECDHE-ECDSA-AES256-SHA384:" \ + "AES128-GCM-SHA256:" \ + "AES256-GCM-SHA384:" \ + "AES128-SHA:" \ + "AES256-SHA:" \ + "AES128-SHA256:" \ + "AES256-SHA256:" \ + "ECDHE-RSA-DES-CBC3-SHA:" \ + "ECDHE-ECDSA-DES-CBC3-SHA:" \ + "DES-CBC3-SHA" + + _protocols = 'all -SSLv2 -SSLv3' + @property def auth_pam_idle_timeout(self): if self._values['auth_pam_idle_timeout'] is None: @@ -300,6 +394,46 @@ class ModuleParameters(Parameters): result = sorted(result) return result + @property + def ssl_cipher_suite(self): + if self._values['ssl_cipher_suite'] is None: + return None + if isinstance(self._values['ssl_cipher_suite'], string_types): + ciphers = self._values['ssl_cipher_suite'].strip() + else: + ciphers = self._values['ssl_cipher_suite'] + if not ciphers: + raise F5ModuleError( + "ssl_cipher_suite may not be set to 'none'" + ) + if ciphers == 'default': + ciphers = ':'.join(sorted(Parameters._ciphers.split(':'))) + elif isinstance(self._values['ssl_cipher_suite'], string_types): + ciphers = ':'.join(sorted(ciphers.split(':'))) + else: + ciphers = ':'.join(sorted(ciphers)) + return ciphers + + @property + def ssl_protocols(self): + if self._values['ssl_protocols'] is None: + return None + if isinstance(self._values['ssl_protocols'], string_types): + protocols = self._values['ssl_protocols'].strip() + else: + protocols = self._values['ssl_protocols'] + if not protocols: + raise F5ModuleError( + "ssl_protocols may not be set to 'none'" + ) + if protocols == 'default': + protocols = ' '.join(sorted(Parameters._protocols.split(' '))) + elif isinstance(protocols, string_types): + protocols = ' '.join(sorted(protocols.split(' '))) + else: + protocols = ' '.join(sorted(protocols)) + return protocols + class ApiParameters(Parameters): @property @@ -331,7 +465,21 @@ class UsableChanges(Changes): class ReportableChanges(Changes): - pass + @property + def ssl_cipher_suite(self): + default = ':'.join(sorted(Parameters._ciphers.split(':'))) + if self._values['ssl_cipher_suite'] == default: + return 'default' + else: + return self._values['ssl_cipher_suite'] + + @property + def ssl_protocols(self): + default = ' '.join(sorted(Parameters._protocols.split(' '))) + if self._values['ssl_protocols'] == default: + return 'default' + else: + return self._values['ssl_protocols'] class Difference(object): @@ -449,7 +597,6 @@ class ModuleManager(object): def update_on_device(self): params = self.changes.api_params() resource = self.client.api.tm.sys.httpd.load() - try: resource.modify(**params) return True @@ -504,7 +651,9 @@ class ArgumentSpec(object): ), redirect_http_to_https=dict( type='bool' - ) + ), + ssl_cipher_suite=dict(type='raw'), + ssl_protocols=dict(type='raw') ) self.argument_spec = {} self.argument_spec.update(f5_argument_spec) diff --git a/lib/ansible/modules/network/f5/bigip_device_ntp.py b/lib/ansible/modules/network/f5/bigip_device_ntp.py index cf1b7f3c132..32cc50d3276 100644 --- a/lib/ansible/modules/network/f5/bigip_device_ntp.py +++ b/lib/ansible/modules/network/f5/bigip_device_ntp.py @@ -17,7 +17,7 @@ module: bigip_device_ntp short_description: Manage NTP servers on a BIG-IP description: - Manage NTP servers on a BIG-IP. -version_added: "2.2" +version_added: 2.2 options: ntp_servers: description: @@ -79,30 +79,23 @@ timezone: from ansible.module_utils.basic import AnsibleModule -HAS_DEVEL_IMPORTS = False - try: - # Sideband repository used for dev 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.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 fqdn_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 - HAS_DEVEL_IMPORTS = True except ImportError: - # Upstream Ansible 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.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 fqdn_name from ansible.module_utils.network.f5.common import f5_argument_spec try: from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError diff --git a/lib/ansible/modules/network/f5/bigip_device_sshd.py b/lib/ansible/modules/network/f5/bigip_device_sshd.py index af180702b52..68591be02f9 100644 --- a/lib/ansible/modules/network/f5/bigip_device_sshd.py +++ b/lib/ansible/modules/network/f5/bigip_device_sshd.py @@ -17,7 +17,7 @@ module: bigip_device_sshd short_description: Manage the SSHD settings of a BIG-IP description: - Manage the SSHD settings of a BIG-IP. -version_added: "2.2" +version_added: 2.2 options: allow: description: @@ -147,30 +147,23 @@ port: from ansible.module_utils.basic import AnsibleModule -HAS_DEVEL_IMPORTS = False - try: - # Sideband repository used for dev 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.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 fqdn_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 - HAS_DEVEL_IMPORTS = True except ImportError: - # Upstream Ansible 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.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 fqdn_name from ansible.module_utils.network.f5.common import f5_argument_spec try: from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError @@ -229,13 +222,33 @@ class Parameters(AnsibleF5Parameters): return result +class ApiParameters(Parameters): + pass + + +class ModuleParameters(Parameters): + pass + + +class Changes(Parameters): + pass + + +class UsableChanges(Changes): + pass + + +class ReportableChanges(Changes): + pass + + 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 = Parameters() + self.want = ModuleParameters(params=self.module.params) + self.changes = UsableChanges() def _update_changed_options(self): changed = {} @@ -246,7 +259,7 @@ class ModuleManager(object): if attr1 != attr2: changed[key] = attr1 if changed: - self.changes = Parameters(params=changed) + self.changes = UsableChanges(params=changed) return True return False @@ -266,7 +279,7 @@ class ModuleManager(object): def read_current_from_device(self): resource = self.client.api.tm.sys.sshd.load() result = resource.attrs - return Parameters(params=result) + return ApiParameters(params=result) def update(self): self.have = self.read_current_from_device() @@ -284,7 +297,7 @@ class ModuleManager(object): return False def update_on_device(self): - params = self.want.api_params() + params = self.changes.api_params() resource = self.client.api.tm.sys.sshd.load() resource.update(**params) diff --git a/lib/ansible/modules/network/f5/bigip_device_trust.py b/lib/ansible/modules/network/f5/bigip_device_trust.py index 22bd0bf8976..2b6e24c85ff 100644 --- a/lib/ansible/modules/network/f5/bigip_device_trust.py +++ b/lib/ansible/modules/network/f5/bigip_device_trust.py @@ -19,7 +19,7 @@ description: - Manage the trust relationships between BIG-IPs. Devices, once peered, cannot be updated. If updating is needed, the peer must first be removed before it can be re-added to the trust. -version_added: "2.5" +version_added: 2.5 options: peer_server: description: @@ -107,30 +107,23 @@ import re from ansible.module_utils.basic import AnsibleModule -HAS_DEVEL_IMPORTS = False - try: - # Sideband repository used for dev 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.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 fqdn_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 - HAS_DEVEL_IMPORTS = True except ImportError: - # Upstream Ansible 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.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 fqdn_name from ansible.module_utils.network.f5.common import f5_argument_spec try: from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError @@ -189,7 +182,7 @@ class Parameters(AnsibleF5Parameters): def peer_hostname(self): if self._values['peer_hostname'] is None: return self.peer_server - regex = re.compile('[^a-zA-Z.-_]') + regex = re.compile(r'[^a-zA-Z0-9.\-_]') result = regex.sub('_', self._values['peer_hostname']) return result diff --git a/lib/ansible/modules/network/f5/bigip_gtm_datacenter.py b/lib/ansible/modules/network/f5/bigip_gtm_datacenter.py index d1860b728e0..6b7b056e685 100644 --- a/lib/ansible/modules/network/f5/bigip_gtm_datacenter.py +++ b/lib/ansible/modules/network/f5/bigip_gtm_datacenter.py @@ -21,7 +21,7 @@ description: where the physical network components reside, such as the server and link objects that share the same subnet on the network. This module is able to manipulate the data center definitions in a BIG-IP. -version_added: "2.2" +version_added: 2.2 options: contact: description: @@ -107,30 +107,23 @@ location: from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import env_fallback -HAS_DEVEL_IMPORTS = False - try: - # Sideband repository used for dev 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.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 fqdn_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 - HAS_DEVEL_IMPORTS = True except ImportError: - # Upstream Ansible 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.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 fqdn_name from ansible.module_utils.network.f5.common import f5_argument_spec try: from ansible.module_utils.network.f5.common import iControlUnexpectedHTTPError diff --git a/test/units/modules/network/f5/fixtures/load_sys_httpd_non_default.json b/test/units/modules/network/f5/fixtures/load_sys_httpd_non_default.json new file mode 100644 index 00000000000..05051b9c081 --- /dev/null +++ b/test/units/modules/network/f5/fixtures/load_sys_httpd_non_default.json @@ -0,0 +1,36 @@ +{ + "kind": "tm:sys:httpd:httpdstate", + "selfLink": "https://localhost/mgmt/tm/sys/httpd?ver=12.1.2", + "allow": [ + "All" + ], + "authName": "BIG-IP", + "authPamDashboardTimeout": "off", + "authPamIdleTimeout": 1200, + "authPamValidateIp": "on", + "fastcgiTimeout": 300, + "fipsCipherVersion": 0, + "hostnameLookup": "off", + "logLevel": "warn", + "maxClients": 10, + "redirectHttpToHttps": "disabled", + "requestBodyMaxTimeout": 0, + "requestBodyMinRate": 500, + "requestBodyTimeout": 60, + "requestHeaderMaxTimeout": 40, + "requestHeaderMinRate": 500, + "requestHeaderTimeout": 20, + "sslCertfile": "/etc/httpd/conf/ssl.crt/server.crt", + "sslCertkeyfile": "/etc/httpd/conf/ssl.key/server.key", + "sslCiphersuite": "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384", + "sslOcspDefaultResponder": "http://127.0.0.1", + "sslOcspEnable": "off", + "sslOcspOverrideResponder": "off", + "sslOcspResponderTimeout": 300, + "sslOcspResponseMaxAge": -1, + "sslOcspResponseTimeSkew": 300, + "sslPort": 443, + "sslProtocol": "all -SSLv2", + "sslVerifyClient": "no", + "sslVerifyDepth": 10 +} diff --git a/test/units/modules/network/f5/test_bigip_device_dns.py b/test/units/modules/network/f5/test_bigip_device_dns.py index 68593528545..895bb69ba5a 100644 --- a/test/units/modules/network/f5/test_bigip_device_dns.py +++ b/test/units/modules/network/f5/test_bigip_device_dns.py @@ -21,9 +21,9 @@ from ansible.compat.tests.mock import patch from ansible.module_utils.basic import AnsibleModule try: - from library.bigip_device_dns import Parameters - from library.bigip_device_dns import ModuleManager - from library.bigip_device_dns import ArgumentSpec + from library.modules.bigip_device_dns import Parameters + from library.modules.bigip_device_dns import ModuleManager + from library.modules.bigip_device_dns 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 @@ -64,7 +64,6 @@ class TestParameters(unittest.TestCase): def test_module_parameters(self): args = dict( cache='disable', - forwarders=['12.12.12.12', '13.13.13.13'], ip_version=4, name_servers=['10.10.10.10', '11.11.11.11'], search=['14.14.14.14', '15.15.15.15'], @@ -87,15 +86,6 @@ class TestParameters(unittest.TestCase): p = Parameters(params=args) assert p.ip_version == 'options inet6' - def test_ensure_forwards_raises_exception(self): - args = dict( - forwarders=['12.12.12.12', '13.13.13.13'], - ) - p = Parameters(params=args) - with pytest.raises(F5ModuleError) as ex: - p.forwarders - assert 'The modifying of forwarders is not supported' in str(ex) - class TestManager(unittest.TestCase): @@ -105,7 +95,6 @@ class TestManager(unittest.TestCase): def test_update_settings(self, *args): set_module_args(dict( cache='disable', - forwarders=['12.12.12.12', '13.13.13.13'], ip_version=4, name_servers=['10.10.10.10', '11.11.11.11'], search=['14.14.14.14', '15.15.15.15'], diff --git a/test/units/modules/network/f5/test_bigip_device_group.py b/test/units/modules/network/f5/test_bigip_device_group.py index a7079409456..789327c22fb 100644 --- a/test/units/modules/network/f5/test_bigip_device_group.py +++ b/test/units/modules/network/f5/test_bigip_device_group.py @@ -20,9 +20,9 @@ from ansible.compat.tests.mock import patch from ansible.module_utils.basic import AnsibleModule try: - from library.bigip_device_group import Parameters - from library.bigip_device_group import ModuleManager - from library.bigip_device_group import ArgumentSpec + from library.modules.bigip_device_group import Parameters + from library.modules.bigip_device_group import ModuleManager + from library.modules.bigip_device_group 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 diff --git a/test/units/modules/network/f5/test_bigip_device_group_member.py b/test/units/modules/network/f5/test_bigip_device_group_member.py index d83c37274bf..84b523192b7 100644 --- a/test/units/modules/network/f5/test_bigip_device_group_member.py +++ b/test/units/modules/network/f5/test_bigip_device_group_member.py @@ -21,9 +21,9 @@ from ansible.compat.tests.mock import patch from ansible.module_utils.basic import AnsibleModule try: - from library.bigip_device_group_member import Parameters - from library.bigip_device_group_member import ModuleManager - from library.bigip_device_group_member import ArgumentSpec + from library.modules.bigip_device_group_member import Parameters + from library.modules.bigip_device_group_member import ModuleManager + from library.modules.bigip_device_group_member 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 diff --git a/test/units/modules/network/f5/test_bigip_device_httpd.py b/test/units/modules/network/f5/test_bigip_device_httpd.py index 49545d9637d..0bbac049ebb 100644 --- a/test/units/modules/network/f5/test_bigip_device_httpd.py +++ b/test/units/modules/network/f5/test_bigip_device_httpd.py @@ -20,9 +20,9 @@ from ansible.compat.tests.mock import patch from ansible.module_utils.basic import AnsibleModule try: - from library.bigip_device_httpd import Parameters - from library.bigip_device_httpd import ModuleManager - from library.bigip_device_httpd import ArgumentSpec + from library.modules.bigip_device_httpd import Parameters + from library.modules.bigip_device_httpd import ModuleManager + from library.modules.bigip_device_httpd 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 @@ -122,3 +122,165 @@ class TestModuleManager(unittest.TestCase): results = mm.exec_module() assert results['changed'] is True + + def test_update_issue_00522(self, *args): + set_module_args( + dict( + ssl_cipher_suite='ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384', + server='localhost', + user='admin', + password='password' + ) + ) + + current = Parameters(params=load_fixture('load_sys_httpd.json')) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + mm = ModuleManager(module=module) + + # Override methods to force specific logic in the module to happen + mm.update_on_device = Mock(return_value=True) + mm.read_current_from_device = Mock(return_value=current) + + results = mm.exec_module() + assert results['changed'] is True + assert results['ssl_cipher_suite'] == 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384' + + def test_update_issue_00522_as_list(self, *args): + set_module_args( + dict( + ssl_cipher_suite=[ + 'ECDHE-RSA-AES128-GCM-SHA256', + 'ECDHE-RSA-AES256-GCM-SHA384' + ], + server='localhost', + user='admin', + password='password' + ) + ) + + current = Parameters(params=load_fixture('load_sys_httpd.json')) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + mm = ModuleManager(module=module) + + # Override methods to force specific logic in the module to happen + mm.update_on_device = Mock(return_value=True) + mm.read_current_from_device = Mock(return_value=current) + + results = mm.exec_module() + assert results['changed'] is True + assert results['ssl_cipher_suite'] == 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384' + + def test_update_issue_00522_default(self, *args): + set_module_args( + dict( + ssl_cipher_suite='default', + server='localhost', + user='admin', + password='password' + ) + ) + + current = Parameters(params=load_fixture('load_sys_httpd_non_default.json')) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + mm = ModuleManager(module=module) + + # Override methods to force specific logic in the module to happen + mm.update_on_device = Mock(return_value=True) + mm.read_current_from_device = Mock(return_value=current) + + results = mm.exec_module() + assert results['changed'] is True + assert results['ssl_cipher_suite'] == 'default' + + def test_update_issue_00587(self, *args): + set_module_args( + dict( + ssl_protocols='all -SSLv2', + server='localhost', + user='admin', + password='password' + ) + ) + + current = Parameters(params=load_fixture('load_sys_httpd.json')) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + mm = ModuleManager(module=module) + + # Override methods to force specific logic in the module to happen + mm.update_on_device = Mock(return_value=True) + mm.read_current_from_device = Mock(return_value=current) + + results = mm.exec_module() + assert results['changed'] is True + assert results['ssl_protocols'] == '-SSLv2 all' + + def test_update_issue_00587_as_list(self, *args): + set_module_args( + dict( + ssl_protocols=[ + 'all', + '-SSLv2' + ], + server='localhost', + user='admin', + password='password' + ) + ) + + current = Parameters(params=load_fixture('load_sys_httpd.json')) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + mm = ModuleManager(module=module) + + # Override methods to force specific logic in the module to happen + mm.update_on_device = Mock(return_value=True) + mm.read_current_from_device = Mock(return_value=current) + + results = mm.exec_module() + assert results['changed'] is True + assert results['ssl_protocols'] == '-SSLv2 all' + + def test_update_issue_00587_default(self, *args): + set_module_args( + dict( + ssl_protocols='default', + server='localhost', + user='admin', + password='password' + ) + ) + + current = Parameters(params=load_fixture('load_sys_httpd_non_default.json')) + + module = AnsibleModule( + argument_spec=self.spec.argument_spec, + supports_check_mode=self.spec.supports_check_mode + ) + mm = ModuleManager(module=module) + + # Override methods to force specific logic in the module to happen + mm.update_on_device = Mock(return_value=True) + mm.read_current_from_device = Mock(return_value=current) + + results = mm.exec_module() + assert results['changed'] is True + assert results['ssl_protocols'] == 'default' diff --git a/test/units/modules/network/f5/test_bigip_device_ntp.py b/test/units/modules/network/f5/test_bigip_device_ntp.py index ddd0297f56d..ce84c9173b9 100644 --- a/test/units/modules/network/f5/test_bigip_device_ntp.py +++ b/test/units/modules/network/f5/test_bigip_device_ntp.py @@ -20,9 +20,9 @@ from ansible.compat.tests.mock import patch from ansible.module_utils.basic import AnsibleModule try: - from library.bigip_device_ntp import Parameters - from library.bigip_device_ntp import ModuleManager - from library.bigip_device_ntp import ArgumentSpec + from library.modules.bigip_device_ntp import Parameters + from library.modules.bigip_device_ntp import ModuleManager + from library.modules.bigip_device_ntp 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 diff --git a/test/units/modules/network/f5/test_bigip_device_sshd.py b/test/units/modules/network/f5/test_bigip_device_sshd.py index 923259f3abf..341c80deceb 100644 --- a/test/units/modules/network/f5/test_bigip_device_sshd.py +++ b/test/units/modules/network/f5/test_bigip_device_sshd.py @@ -20,15 +20,17 @@ from ansible.compat.tests.mock import patch from ansible.module_utils.basic import AnsibleModule try: - from library.bigip_device_sshd import Parameters - from library.bigip_device_sshd import ModuleManager - from library.bigip_device_sshd import ArgumentSpec + from library.modules.bigip_device_sshd import ApiParameters + from library.modules.bigip_device_sshd import ModuleParameters + from library.modules.bigip_device_sshd import ModuleManager + from library.modules.bigip_device_sshd 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 except ImportError: try: - from ansible.modules.network.f5.bigip_device_sshd import Parameters + from ansible.modules.network.f5.bigip_device_sshd import ApiParameters + from ansible.modules.network.f5.bigip_device_sshd import ModuleParameters from ansible.modules.network.f5.bigip_device_sshd import ModuleManager from ansible.modules.network.f5.bigip_device_sshd import ArgumentSpec from ansible.module_utils.network.f5.common import F5ModuleError @@ -73,7 +75,7 @@ class TestParameters(unittest.TestCase): user='admin', password='password' ) - p = Parameters(params=args) + p = ModuleParameters(params=args) assert p.allow == ['all'] assert p.banner == 'enabled' assert p.banner_text == 'asdf' @@ -104,8 +106,8 @@ class TestManager(unittest.TestCase): # Configure the parameters that would be returned by querying the # remote device - current = Parameters( - dict( + current = ApiParameters( + params=dict( allow=['172.27.1.1'] ) ) diff --git a/test/units/modules/network/f5/test_bigip_device_trust.py b/test/units/modules/network/f5/test_bigip_device_trust.py index 3191d3a69b8..3d93977ae48 100644 --- a/test/units/modules/network/f5/test_bigip_device_trust.py +++ b/test/units/modules/network/f5/test_bigip_device_trust.py @@ -20,11 +20,11 @@ from ansible.compat.tests.mock import patch from ansible.module_utils.basic import AnsibleModule try: - from library.bigip_device_trust import Parameters - from library.bigip_device_trust import ModuleManager - from library.bigip_device_trust import ArgumentSpec - from library.bigip_device_trust import HAS_F5SDK - from library.bigip_device_trust import HAS_NETADDR + from library.modules.bigip_device_trust import Parameters + from library.modules.bigip_device_trust import ModuleManager + from library.modules.bigip_device_trust import ArgumentSpec + from library.modules.bigip_device_trust import HAS_F5SDK + from library.modules.bigip_device_trust import HAS_NETADDR 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 @@ -114,6 +114,22 @@ class TestParameters(unittest.TestCase): assert p.peer_password == 'secret' assert p.type is False + def test_hyphenated_peer_hostname(self): + args = dict( + peer_hostname='hn---hyphen____underscore.hmatsuda.local', + ) + + p = Parameters(params=args) + assert p.peer_hostname == 'hn---hyphen____underscore.hmatsuda.local' + + def test_numbered_peer_hostname(self): + args = dict( + peer_hostname='BIG-IP_12x_ans2.example.local', + ) + + p = Parameters(params=args) + assert p.peer_hostname == 'BIG-IP_12x_ans2.example.local' + class TestManager(unittest.TestCase): diff --git a/test/units/modules/network/f5/test_bigip_gtm_datacenter.py b/test/units/modules/network/f5/test_bigip_gtm_datacenter.py index 1484b8ece86..79d2c80d833 100644 --- a/test/units/modules/network/f5/test_bigip_gtm_datacenter.py +++ b/test/units/modules/network/f5/test_bigip_gtm_datacenter.py @@ -20,10 +20,10 @@ from ansible.compat.tests.mock import patch from ansible.module_utils.basic import AnsibleModule try: - from library.bigip_gtm_datacenter import ApiParameters - from library.bigip_gtm_datacenter import ModuleParameters - from library.bigip_gtm_datacenter import ModuleManager - from library.bigip_gtm_datacenter import ArgumentSpec + from library.modules.bigip_gtm_datacenter import ApiParameters + from library.modules.bigip_gtm_datacenter import ModuleParameters + from library.modules.bigip_gtm_datacenter import ModuleManager + from library.modules.bigip_gtm_datacenter 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