diff --git a/lib/ansible/modules/network/fortios/fortios_firewall_ssl_ssh_profile.py b/lib/ansible/modules/network/fortios/fortios_firewall_ssl_ssh_profile.py index dfd6859dd5b..dfd34adee2b 100644 --- a/lib/ansible/modules/network/fortios/fortios_firewall_ssl_ssh_profile.py +++ b/lib/ansible/modules/network/fortios/fortios_firewall_ssl_ssh_profile.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_firewall_ssl_ssh_profile short_description: Configure SSL/SSH protocol options in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure firewall feature and ssl_ssh_profile category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify firewall feature and ssl_ssh_profile category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,82 +41,104 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip adress. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 firewall_ssl_ssh_profile: description: - Configure SSL/SSH protocol options. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent caname: description: - CA certificate used by SSL Inspection. Source vpn.certificate.local.name. + type: str comment: description: - Optional comments. + type: str ftps: description: - Configure FTPS options. + type: dict suboptions: - allow-invalid-server-cert: + allow_invalid_server_cert: description: - When enabled, allows SSL sessions whose server certificate validation failed. + type: str choices: - enable - disable - client-cert-request: + client_cert_request: description: - Action based on client certificate request. + type: str choices: - bypass - inspect - block ports: description: - - Ports to use for scanning (1 - 65535, default = 443). + - Ports to use for scanning (1 - 65535). + type: int status: description: - Configure protocol inspection status. + type: str choices: - disable - deep-inspection - unsupported-ssl: + unsupported_ssl: description: - Action based on the SSL encryption used being unsupported. + type: str choices: - bypass - inspect - block - untrusted-cert: + untrusted_cert: description: - Allow, ignore, or block the untrusted SSL session server certificate. + type: str choices: - allow - block @@ -127,40 +146,47 @@ options: https: description: - Configure HTTPS options. + type: dict suboptions: - allow-invalid-server-cert: + allow_invalid_server_cert: description: - When enabled, allows SSL sessions whose server certificate validation failed. + type: str choices: - enable - disable - client-cert-request: + client_cert_request: description: - Action based on client certificate request. + type: str choices: - bypass - inspect - block ports: description: - - Ports to use for scanning (1 - 65535, default = 443). + - Ports to use for scanning (1 - 65535). + type: int status: description: - Configure protocol inspection status. + type: str choices: - disable - certificate-inspection - deep-inspection - unsupported-ssl: + unsupported_ssl: description: - Action based on the SSL encryption used being unsupported. + type: str choices: - bypass - inspect - block - untrusted-cert: + untrusted_cert: description: - Allow, ignore, or block the untrusted SSL session server certificate. + type: str choices: - allow - block @@ -168,46 +194,54 @@ options: imaps: description: - Configure IMAPS options. + type: dict suboptions: - allow-invalid-server-cert: + allow_invalid_server_cert: description: - When enabled, allows SSL sessions whose server certificate validation failed. + type: str choices: - enable - disable - client-cert-request: + client_cert_request: description: - Action based on client certificate request. + type: str choices: - bypass - inspect - block ports: description: - - Ports to use for scanning (1 - 65535, default = 443). + - Ports to use for scanning (1 - 65535). + type: int status: description: - Configure protocol inspection status. + type: str choices: - disable - deep-inspection - unsupported-ssl: + unsupported_ssl: description: - Action based on the SSL encryption used being unsupported. + type: str choices: - bypass - inspect - block - untrusted-cert: + untrusted_cert: description: - Allow, ignore, or block the untrusted SSL session server certificate. + type: str choices: - allow - block - ignore - mapi-over-https: + mapi_over_https: description: - Enable/disable inspection of MAPI over HTTPS. + type: str choices: - enable - disable @@ -215,97 +249,115 @@ options: description: - Name. required: true + type: str pop3s: description: - Configure POP3S options. + type: dict suboptions: - allow-invalid-server-cert: + allow_invalid_server_cert: description: - When enabled, allows SSL sessions whose server certificate validation failed. + type: str choices: - enable - disable - client-cert-request: + client_cert_request: description: - Action based on client certificate request. + type: str choices: - bypass - inspect - block ports: description: - - Ports to use for scanning (1 - 65535, default = 443). + - Ports to use for scanning (1 - 65535). + type: int status: description: - Configure protocol inspection status. + type: str choices: - disable - deep-inspection - unsupported-ssl: + unsupported_ssl: description: - Action based on the SSL encryption used being unsupported. + type: str choices: - bypass - inspect - block - untrusted-cert: + untrusted_cert: description: - Allow, ignore, or block the untrusted SSL session server certificate. + type: str choices: - allow - block - ignore - rpc-over-https: + rpc_over_https: description: - Enable/disable inspection of RPC over HTTPS. + type: str choices: - enable - disable - server-cert: + server_cert: description: - Certificate used by SSL Inspection to replace server certificate. Source vpn.certificate.local.name. - server-cert-mode: + type: str + server_cert_mode: description: - Re-sign or replace the server's certificate. + type: str choices: - re-sign - replace smtps: description: - Configure SMTPS options. + type: dict suboptions: - allow-invalid-server-cert: + allow_invalid_server_cert: description: - When enabled, allows SSL sessions whose server certificate validation failed. + type: str choices: - enable - disable - client-cert-request: + client_cert_request: description: - Action based on client certificate request. + type: str choices: - bypass - inspect - block ports: description: - - Ports to use for scanning (1 - 65535, default = 443). + - Ports to use for scanning (1 - 65535). + type: int status: description: - Configure protocol inspection status. + type: str choices: - disable - deep-inspection - unsupported-ssl: + unsupported_ssl: description: - Action based on the SSL encryption used being unsupported. + type: str choices: - bypass - inspect - block - untrusted-cert: + untrusted_cert: description: - Allow, ignore, or block the untrusted SSL session server certificate. + type: str choices: - allow - block @@ -313,142 +365,169 @@ options: ssh: description: - Configure SSH options. + type: dict suboptions: - inspect-all: + inspect_all: description: - Level of SSL inspection. + type: str choices: - disable - deep-inspection ports: description: - - Ports to use for scanning (1 - 65535, default = 443). - ssh-algorithm: + - Ports to use for scanning (1 - 65535). + type: int + ssh_algorithm: description: - Relative strength of encryption algorithms accepted during negotiation. + type: str choices: - compatible - high-encryption - ssh-policy-check: + ssh_policy_check: description: - Enable/disable SSH policy check. + type: str choices: - disable - enable - ssh-tun-policy-check: + ssh_tun_policy_check: description: - Enable/disable SSH tunnel policy check. + type: str choices: - disable - enable status: description: - Configure protocol inspection status. + type: str choices: - disable - deep-inspection - unsupported-version: + unsupported_version: description: - Action based on SSH version being unsupported. + type: str choices: - bypass - block ssl: description: - Configure SSL options. + type: dict suboptions: - allow-invalid-server-cert: + allow_invalid_server_cert: description: - When enabled, allows SSL sessions whose server certificate validation failed. + type: str choices: - enable - disable - client-cert-request: + client_cert_request: description: - Action based on client certificate request. + type: str choices: - bypass - inspect - block - inspect-all: + inspect_all: description: - Level of SSL inspection. + type: str choices: - disable - certificate-inspection - deep-inspection - unsupported-ssl: + unsupported_ssl: description: - Action based on the SSL encryption used being unsupported. + type: str choices: - bypass - inspect - block - untrusted-cert: + untrusted_cert: description: - Allow, ignore, or block the untrusted SSL session server certificate. + type: str choices: - allow - block - ignore - ssl-anomalies-log: + ssl_anomalies_log: description: - Enable/disable logging SSL anomalies. + type: str choices: - disable - enable - ssl-exempt: + ssl_exempt: description: - Servers to exempt from SSL inspection. + type: list suboptions: address: description: - IPv4 address object. Source firewall.address.name firewall.addrgrp.name. + type: str address6: description: - IPv6 address object. Source firewall.address6.name firewall.addrgrp6.name. - fortiguard-category: + type: str + fortiguard_category: description: - FortiGuard category ID. + type: int id: description: - ID number. required: true + type: int regex: description: - Exempt servers by regular expression. + type: str type: description: - Type of address object (IPv4 or IPv6) or FortiGuard category. + type: str choices: - fortiguard-category - address - address6 - wildcard-fqdn - regex - wildcard-fqdn: + wildcard_fqdn: description: - Exempt servers by wildcard FQDN. Source firewall.wildcard-fqdn.custom.name firewall.wildcard-fqdn.group.name. - ssl-exemptions-log: + type: str + ssl_exemptions_log: description: - Enable/disable logging SSL exemptions. + type: str choices: - disable - enable - ssl-server: + ssl_server: description: - SSL servers. + type: list suboptions: - ftps-client-cert-request: + ftps_client_cert_request: description: - Action based on client certificate request during the FTPS handshake. + type: str choices: - bypass - inspect - block - https-client-cert-request: + https_client_cert_request: description: - Action based on client certificate request during the HTTPS handshake. + type: str choices: - bypass - inspect @@ -457,9 +536,11 @@ options: description: - SSL server ID. required: true - imaps-client-cert-request: + type: int + imaps_client_cert_request: description: - Action based on client certificate request during the IMAPS handshake. + type: str choices: - bypass - inspect @@ -467,39 +548,46 @@ options: ip: description: - IPv4 address of the SSL server. - pop3s-client-cert-request: + type: str + pop3s_client_cert_request: description: - Action based on client certificate request during the POP3S handshake. + type: str choices: - bypass - inspect - block - smtps-client-cert-request: + smtps_client_cert_request: description: - Action based on client certificate request during the SMTPS handshake. + type: str choices: - bypass - inspect - block - ssl-other-client-cert-request: + ssl_other_client_cert_request: description: - Action based on client certificate request during an SSL protocol handshake. + type: str choices: - bypass - inspect - block - untrusted-caname: + untrusted_caname: description: - Untrusted CA certificate used by SSL Inspection. Source vpn.certificate.local.name. - use-ssl-server: + type: str + use_ssl_server: description: - Enable/disable the use of SSL server table for SSL offloading. + type: str choices: - disable - enable whitelist: description: - Enable/disable exempting servers by FortiGuard whitelist. + type: str choices: - enable - disable @@ -512,6 +600,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure SSL/SSH protocol options. fortios_firewall_ssl_ssh_profile: @@ -520,87 +609,87 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" firewall_ssl_ssh_profile: - state: "present" caname: " (source vpn.certificate.local.name)" comment: "Optional comments." ftps: - allow-invalid-server-cert: "enable" - client-cert-request: "bypass" + allow_invalid_server_cert: "enable" + client_cert_request: "bypass" ports: "8" status: "disable" - unsupported-ssl: "bypass" - untrusted-cert: "allow" + unsupported_ssl: "bypass" + untrusted_cert: "allow" https: - allow-invalid-server-cert: "enable" - client-cert-request: "bypass" + allow_invalid_server_cert: "enable" + client_cert_request: "bypass" ports: "15" status: "disable" - unsupported-ssl: "bypass" - untrusted-cert: "allow" + unsupported_ssl: "bypass" + untrusted_cert: "allow" imaps: - allow-invalid-server-cert: "enable" - client-cert-request: "bypass" + allow_invalid_server_cert: "enable" + client_cert_request: "bypass" ports: "22" status: "disable" - unsupported-ssl: "bypass" - untrusted-cert: "allow" - mapi-over-https: "enable" + unsupported_ssl: "bypass" + untrusted_cert: "allow" + mapi_over_https: "enable" name: "default_name_27" pop3s: - allow-invalid-server-cert: "enable" - client-cert-request: "bypass" + allow_invalid_server_cert: "enable" + client_cert_request: "bypass" ports: "31" status: "disable" - unsupported-ssl: "bypass" - untrusted-cert: "allow" - rpc-over-https: "enable" - server-cert: " (source vpn.certificate.local.name)" - server-cert-mode: "re-sign" + unsupported_ssl: "bypass" + untrusted_cert: "allow" + rpc_over_https: "enable" + server_cert: " (source vpn.certificate.local.name)" + server_cert_mode: "re-sign" smtps: - allow-invalid-server-cert: "enable" - client-cert-request: "bypass" + allow_invalid_server_cert: "enable" + client_cert_request: "bypass" ports: "41" status: "disable" - unsupported-ssl: "bypass" - untrusted-cert: "allow" + unsupported_ssl: "bypass" + untrusted_cert: "allow" ssh: - inspect-all: "disable" + inspect_all: "disable" ports: "47" - ssh-algorithm: "compatible" - ssh-policy-check: "disable" - ssh-tun-policy-check: "disable" + ssh_algorithm: "compatible" + ssh_policy_check: "disable" + ssh_tun_policy_check: "disable" status: "disable" - unsupported-version: "bypass" + unsupported_version: "bypass" ssl: - allow-invalid-server-cert: "enable" - client-cert-request: "bypass" - inspect-all: "disable" - unsupported-ssl: "bypass" - untrusted-cert: "allow" - ssl-anomalies-log: "disable" - ssl-exempt: + allow_invalid_server_cert: "enable" + client_cert_request: "bypass" + inspect_all: "disable" + unsupported_ssl: "bypass" + untrusted_cert: "allow" + ssl_anomalies_log: "disable" + ssl_exempt: - address: " (source firewall.address.name firewall.addrgrp.name)" address6: " (source firewall.address6.name firewall.addrgrp6.name)" - fortiguard-category: "63" + fortiguard_category: "63" id: "64" regex: "" type: "fortiguard-category" - wildcard-fqdn: " (source firewall.wildcard-fqdn.custom.name firewall.wildcard-fqdn.group.name)" - ssl-exemptions-log: "disable" - ssl-server: + wildcard_fqdn: " (source firewall.wildcard-fqdn.custom.name firewall.wildcard-fqdn.group.name)" + ssl_exemptions_log: "disable" + ssl_server: - - ftps-client-cert-request: "bypass" - https-client-cert-request: "bypass" + ftps_client_cert_request: "bypass" + https_client_cert_request: "bypass" id: "72" - imaps-client-cert-request: "bypass" + imaps_client_cert_request: "bypass" ip: "" - pop3s-client-cert-request: "bypass" - smtps-client-cert-request: "bypass" - ssl-other-client-cert-request: "bypass" - untrusted-caname: " (source vpn.certificate.local.name)" - use-ssl-server: "disable" + pop3s_client_cert_request: "bypass" + smtps_client_cert_request: "bypass" + ssl_other_client_cert_request: "bypass" + untrusted_caname: " (source vpn.certificate.local.name)" + use_ssl_server: "disable" whitelist: "enable" ''' @@ -664,14 +753,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -679,17 +770,17 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_firewall_ssl_ssh_profile_data(json): option_list = ['caname', 'comment', 'ftps', - 'https', 'imaps', 'mapi-over-https', - 'name', 'pop3s', 'rpc-over-https', - 'server-cert', 'server-cert-mode', 'smtps', - 'ssh', 'ssl', 'ssl-anomalies-log', - 'ssl-exempt', 'ssl-exemptions-log', 'ssl-server', - 'untrusted-caname', 'use-ssl-server', 'whitelist'] + 'https', 'imaps', 'mapi_over_https', + 'name', 'pop3s', 'rpc_over_https', + 'server_cert', 'server_cert_mode', 'smtps', + 'ssh', 'ssl', 'ssl_anomalies_log', + 'ssl_exempt', 'ssl_exemptions_log', 'ssl_server', + 'untrusted_caname', 'use_ssl_server', 'whitelist'] dictionary = {} for attribute in option_list: @@ -699,192 +790,210 @@ def filter_firewall_ssl_ssh_profile_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def firewall_ssl_ssh_profile(data, fos): vdom = data['vdom'] + state = data['state'] firewall_ssl_ssh_profile_data = data['firewall_ssl_ssh_profile'] - filtered_data = filter_firewall_ssl_ssh_profile_data(firewall_ssl_ssh_profile_data) - if firewall_ssl_ssh_profile_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_firewall_ssl_ssh_profile_data(firewall_ssl_ssh_profile_data)) + + if state == "present": return fos.set('firewall', 'ssl-ssh-profile', data=filtered_data, vdom=vdom) - elif firewall_ssl_ssh_profile_data['state'] == "absent": + elif state == "absent": return fos.delete('firewall', 'ssl-ssh-profile', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_firewall(data, fos): - login(data) - methodlist = ['firewall_ssl_ssh_profile'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['firewall_ssl_ssh_profile']: + resp = firewall_ssl_ssh_profile(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "firewall_ssl_ssh_profile": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "caname": {"required": False, "type": "str"}, "comment": {"required": False, "type": "str"}, "ftps": {"required": False, "type": "dict", "options": { - "allow-invalid-server-cert": {"required": False, "type": "str", + "allow_invalid_server_cert": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "client-cert-request": {"required": False, "type": "str", + "client_cert_request": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, "ports": {"required": False, "type": "int"}, "status": {"required": False, "type": "str", "choices": ["disable", "deep-inspection"]}, - "unsupported-ssl": {"required": False, "type": "str", + "unsupported_ssl": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, - "untrusted-cert": {"required": False, "type": "str", + "untrusted_cert": {"required": False, "type": "str", "choices": ["allow", "block", "ignore"]} }}, "https": {"required": False, "type": "dict", "options": { - "allow-invalid-server-cert": {"required": False, "type": "str", + "allow_invalid_server_cert": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "client-cert-request": {"required": False, "type": "str", + "client_cert_request": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, "ports": {"required": False, "type": "int"}, "status": {"required": False, "type": "str", "choices": ["disable", "certificate-inspection", "deep-inspection"]}, - "unsupported-ssl": {"required": False, "type": "str", + "unsupported_ssl": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, - "untrusted-cert": {"required": False, "type": "str", + "untrusted_cert": {"required": False, "type": "str", "choices": ["allow", "block", "ignore"]} }}, "imaps": {"required": False, "type": "dict", "options": { - "allow-invalid-server-cert": {"required": False, "type": "str", + "allow_invalid_server_cert": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "client-cert-request": {"required": False, "type": "str", + "client_cert_request": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, "ports": {"required": False, "type": "int"}, "status": {"required": False, "type": "str", "choices": ["disable", "deep-inspection"]}, - "unsupported-ssl": {"required": False, "type": "str", + "unsupported_ssl": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, - "untrusted-cert": {"required": False, "type": "str", + "untrusted_cert": {"required": False, "type": "str", "choices": ["allow", "block", "ignore"]} }}, - "mapi-over-https": {"required": False, "type": "str", + "mapi_over_https": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "name": {"required": True, "type": "str"}, "pop3s": {"required": False, "type": "dict", "options": { - "allow-invalid-server-cert": {"required": False, "type": "str", + "allow_invalid_server_cert": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "client-cert-request": {"required": False, "type": "str", + "client_cert_request": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, "ports": {"required": False, "type": "int"}, "status": {"required": False, "type": "str", "choices": ["disable", "deep-inspection"]}, - "unsupported-ssl": {"required": False, "type": "str", + "unsupported_ssl": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, - "untrusted-cert": {"required": False, "type": "str", + "untrusted_cert": {"required": False, "type": "str", "choices": ["allow", "block", "ignore"]} }}, - "rpc-over-https": {"required": False, "type": "str", + "rpc_over_https": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "server-cert": {"required": False, "type": "str"}, - "server-cert-mode": {"required": False, "type": "str", + "server_cert": {"required": False, "type": "str"}, + "server_cert_mode": {"required": False, "type": "str", "choices": ["re-sign", "replace"]}, "smtps": {"required": False, "type": "dict", "options": { - "allow-invalid-server-cert": {"required": False, "type": "str", + "allow_invalid_server_cert": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "client-cert-request": {"required": False, "type": "str", + "client_cert_request": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, "ports": {"required": False, "type": "int"}, "status": {"required": False, "type": "str", "choices": ["disable", "deep-inspection"]}, - "unsupported-ssl": {"required": False, "type": "str", + "unsupported_ssl": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, - "untrusted-cert": {"required": False, "type": "str", + "untrusted_cert": {"required": False, "type": "str", "choices": ["allow", "block", "ignore"]} }}, "ssh": {"required": False, "type": "dict", "options": { - "inspect-all": {"required": False, "type": "str", + "inspect_all": {"required": False, "type": "str", "choices": ["disable", "deep-inspection"]}, "ports": {"required": False, "type": "int"}, - "ssh-algorithm": {"required": False, "type": "str", + "ssh_algorithm": {"required": False, "type": "str", "choices": ["compatible", "high-encryption"]}, - "ssh-policy-check": {"required": False, "type": "str", + "ssh_policy_check": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "ssh-tun-policy-check": {"required": False, "type": "str", + "ssh_tun_policy_check": {"required": False, "type": "str", "choices": ["disable", "enable"]}, "status": {"required": False, "type": "str", "choices": ["disable", "deep-inspection"]}, - "unsupported-version": {"required": False, "type": "str", + "unsupported_version": {"required": False, "type": "str", "choices": ["bypass", "block"]} }}, "ssl": {"required": False, "type": "dict", "options": { - "allow-invalid-server-cert": {"required": False, "type": "str", + "allow_invalid_server_cert": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "client-cert-request": {"required": False, "type": "str", + "client_cert_request": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, - "inspect-all": {"required": False, "type": "str", + "inspect_all": {"required": False, "type": "str", "choices": ["disable", "certificate-inspection", "deep-inspection"]}, - "unsupported-ssl": {"required": False, "type": "str", + "unsupported_ssl": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, - "untrusted-cert": {"required": False, "type": "str", + "untrusted_cert": {"required": False, "type": "str", "choices": ["allow", "block", "ignore"]} }}, - "ssl-anomalies-log": {"required": False, "type": "str", + "ssl_anomalies_log": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "ssl-exempt": {"required": False, "type": "list", + "ssl_exempt": {"required": False, "type": "list", "options": { "address": {"required": False, "type": "str"}, "address6": {"required": False, "type": "str"}, - "fortiguard-category": {"required": False, "type": "int"}, + "fortiguard_category": {"required": False, "type": "int"}, "id": {"required": True, "type": "int"}, "regex": {"required": False, "type": "str"}, "type": {"required": False, "type": "str", "choices": ["fortiguard-category", "address", "address6", "wildcard-fqdn", "regex"]}, - "wildcard-fqdn": {"required": False, "type": "str"} + "wildcard_fqdn": {"required": False, "type": "str"} }}, - "ssl-exemptions-log": {"required": False, "type": "str", + "ssl_exemptions_log": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "ssl-server": {"required": False, "type": "list", + "ssl_server": {"required": False, "type": "list", "options": { - "ftps-client-cert-request": {"required": False, "type": "str", + "ftps_client_cert_request": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, - "https-client-cert-request": {"required": False, "type": "str", + "https_client_cert_request": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, "id": {"required": True, "type": "int"}, - "imaps-client-cert-request": {"required": False, "type": "str", + "imaps_client_cert_request": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, "ip": {"required": False, "type": "str"}, - "pop3s-client-cert-request": {"required": False, "type": "str", + "pop3s_client_cert_request": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, - "smtps-client-cert-request": {"required": False, "type": "str", + "smtps_client_cert_request": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]}, - "ssl-other-client-cert-request": {"required": False, "type": "str", + "ssl_other_client_cert_request": {"required": False, "type": "str", "choices": ["bypass", "inspect", "block"]} }}, - "untrusted-caname": {"required": False, "type": "str"}, - "use-ssl-server": {"required": False, "type": "str", + "untrusted_caname": {"required": False, "type": "str"}, + "use_ssl_server": {"required": False, "type": "str", "choices": ["disable", "enable"]}, "whitelist": {"required": False, "type": "str", "choices": ["enable", "disable"]} @@ -895,15 +1004,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_firewall(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_firewall(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_firewall(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_firewall_ttl_policy.py b/lib/ansible/modules/network/fortios/fortios_firewall_ttl_policy.py index 3cb04e804e9..941c2db677a 100644 --- a/lib/ansible/modules/network/fortios/fortios_firewall_ttl_policy.py +++ b/lib/ansible/modules/network/fortios/fortios_firewall_ttl_policy.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_firewall_ttl_policy short_description: Configure TTL policies in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure firewall feature and ttl_policy category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify firewall feature and ttl_policy category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,43 +41,57 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip adress. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 firewall_ttl_policy: description: - Configure TTL policies. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent action: description: - - Action to be performed on traffic matching this policy (default = deny). + - Action to be performed on traffic matching this policy . + type: str choices: - accept - deny @@ -88,38 +99,47 @@ options: description: - ID. required: true + type: int schedule: description: - Schedule object from available options. Source firewall.schedule.onetime.name firewall.schedule.recurring.name firewall.schedule.group .name. + type: str service: description: - Service object(s) from available options. Separate multiple names with a space. + type: list suboptions: name: description: - Service name. Source firewall.service.custom.name firewall.service.group.name. required: true + type: str srcaddr: description: - Source address object(s) from available options. Separate multiple names with a space. + type: list suboptions: name: description: - Address name. Source firewall.address.name firewall.addrgrp.name. required: true + type: str srcintf: description: - Source interface name from available interfaces. Source system.zone.name system.interface.name. + type: str status: description: - Enable/disable this TTL policy. + type: str choices: - enable - disable ttl: description: - "Value/range to match against the packet's Time to Live value (format: ttl[ - ttl_high], 1 - 255)." + type: str ''' EXAMPLES = ''' @@ -129,6 +149,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure TTL policies. fortios_firewall_ttl_policy: @@ -137,8 +158,8 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" firewall_ttl_policy: - state: "present" action: "accept" id: "4" schedule: " (source firewall.schedule.onetime.name firewall.schedule.recurring.name firewall.schedule.group.name)" @@ -213,14 +234,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -228,7 +251,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_firewall_ttl_policy_data(json): @@ -244,48 +267,66 @@ def filter_firewall_ttl_policy_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def firewall_ttl_policy(data, fos): vdom = data['vdom'] + state = data['state'] firewall_ttl_policy_data = data['firewall_ttl_policy'] - filtered_data = filter_firewall_ttl_policy_data(firewall_ttl_policy_data) - if firewall_ttl_policy_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_firewall_ttl_policy_data(firewall_ttl_policy_data)) + + if state == "present": return fos.set('firewall', 'ttl-policy', data=filtered_data, vdom=vdom) - elif firewall_ttl_policy_data['state'] == "absent": + elif state == "absent": return fos.delete('firewall', 'ttl-policy', mkey=filtered_data['id'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_firewall(data, fos): - login(data) - methodlist = ['firewall_ttl_policy'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['firewall_ttl_policy']: + resp = firewall_ttl_policy(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "firewall_ttl_policy": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "action": {"required": False, "type": "str", "choices": ["accept", "deny"]}, "id": {"required": True, "type": "int"}, @@ -309,15 +350,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_firewall(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_firewall(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_firewall(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_firewall_vip.py b/lib/ansible/modules/network/fortios/fortios_firewall_vip.py index 86f9b44fb50..79a09f002ef 100644 --- a/lib/ansible/modules/network/fortios/fortios_firewall_vip.py +++ b/lib/ansible/modules/network/fortios/fortios_firewall_vip.py @@ -1,6 +1,6 @@ #!/usr/bin/python from __future__ import (absolute_import, division, print_function) -# Copyright 2018 Fortinet, Inc. +# Copyright 2019 Fortinet, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_firewall_vip short_description: Configure virtual IP for IPv4 in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure firewall feature and vip category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify firewall feature and vip category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,129 +41,164 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. type: bool - default: false + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. + type: bool + default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 firewall_vip: description: - Configure virtual IP for IPv4. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent - arp-reply: + arp_reply: description: - Enable to respond to ARP requests for this virtual IP address. Enabled by default. + type: str choices: - disable - enable color: description: - Color of icon on the GUI. + type: int comment: description: - Comment. - dns-mapping-ttl: + type: str + dns_mapping_ttl: description: - - DNS mapping TTL (Set to zero to use TTL in DNS response, default = 0). + - DNS mapping TTL (Set to zero to use TTL in DNS response). + type: int extaddr: description: - External FQDN address name. + type: list suboptions: name: description: - Address name. Source firewall.address.name firewall.addrgrp.name. required: true + type: str extintf: description: - Interface connected to the source network that receives the packets that will be forwarded to the destination network. Source system .interface.name. + type: str extip: description: - IP address or address range on the external interface that you want to map to an address or address range on the destination network. + type: str extport: description: - Incoming port number range that you want to map to a port number range on the destination network. - gratuitous-arp-interval: + type: str + gratuitous_arp_interval: description: - Enable to have the VIP send gratuitous ARPs. 0=disabled. Set from 5 up to 8640000 seconds to enable. - http-cookie-age: + type: int + http_cookie_age: description: - Time in minutes that client web browsers should keep a cookie. Default is 60 seconds. 0 = no time limit. - http-cookie-domain: + type: int + http_cookie_domain: description: - Domain that HTTP cookie persistence should apply to. - http-cookie-domain-from-host: + type: str + http_cookie_domain_from_host: description: - Enable/disable use of HTTP cookie domain from host field in HTTP. + type: str choices: - disable - enable - http-cookie-generation: + http_cookie_generation: description: - Generation of HTTP cookie to be accepted. Changing invalidates all existing cookies. - http-cookie-path: + type: int + http_cookie_path: description: - Limit HTTP cookie persistence to the specified path. - http-cookie-share: + type: str + http_cookie_share: description: - Control sharing of cookies across virtual servers. same-ip means a cookie from one virtual server can be used by another. Disable stops cookie sharing. + type: str choices: - disable - same-ip - http-ip-header: + http_ip_header: description: - For HTTP multiplexing, enable to add the original client IP address in the XForwarded-For HTTP header. + type: str choices: - enable - disable - http-ip-header-name: + http_ip_header_name: description: - For HTTP multiplexing, enter a custom HTTPS header name. The original client IP address is added to this header. If empty, X-Forwarded-For is used. - http-multiplex: + type: str + http_multiplex: description: - Enable/disable HTTP multiplexing. + type: str choices: - enable - disable - https-cookie-secure: + https_cookie_secure: description: - Enable/disable verification that inserted HTTPS cookies are secure. + type: str choices: - disable - enable id: description: - Custom defined ID. - ldb-method: + type: int + ldb_method: description: - Method used to distribute sessions to real servers. + type: str choices: - static - round-robin @@ -175,50 +207,61 @@ options: - least-rtt - first-alive - http-host - mapped-addr: + mapped_addr: description: - Mapped FQDN address name. Source firewall.address.name. + type: str mappedip: description: - IP address or address range on the destination network to which the external IP address is mapped. + type: list suboptions: range: description: - Mapped IP range. required: true + type: str mappedport: description: - Port number range on the destination network to which the external port number range is mapped. - max-embryonic-connections: + type: str + max_embryonic_connections: description: - Maximum number of incomplete connections. + type: int monitor: description: - Name of the health check monitor to use when polling to determine a virtual server's connectivity status. + type: list suboptions: name: description: - Health monitor name. Source firewall.ldb-monitor.name. required: true + type: str name: description: - Virtual IP name. required: true - nat-source-vip: + type: str + nat_source_vip: description: - - Enable to prevent unintended servers from using a virtual IP. Disable to use the actual IP address of the server as the source address. + - Enable/disable forcing the source NAT mapped IP to the external IP for all traffic. + type: str choices: - disable - enable - outlook-web-access: + outlook_web_access: description: - Enable to add the Front-End-Https header for Microsoft Outlook Web Access. + type: str choices: - disable - enable persistence: description: - Configure how to make sure that clients connect to the same server every time they make a request that is part of the same session. + type: str choices: - none - http-cookie @@ -226,18 +269,21 @@ options: portforward: description: - Enable/disable port forwarding. + type: str choices: - disable - enable - portmapping-type: + portmapping_type: description: - Port mapping type. + type: str choices: - 1-to-1 - m-to-n protocol: description: - Protocol to use when forwarding packets. + type: str choices: - tcp - udp @@ -246,43 +292,54 @@ options: realservers: description: - Select the real servers that this server load balancing VIP will distribute traffic to. + type: list suboptions: - client-ip: + client_ip: description: - Only clients in this IP range can connect to this real server. + type: str healthcheck: description: - Enable to check the responsiveness of the real server before forwarding traffic. + type: str choices: - disable - enable - vip - holddown-interval: + holddown_interval: description: - Time in seconds that the health check monitor continues to monitor and unresponsive server that should be active. - http-host: + type: int + http_host: description: - HTTP server domain name in HTTP header. + type: str id: description: - Real server ID. required: true + type: int ip: description: - IP address of the real server. - max-connections: + type: str + max_connections: description: - Max number of active connections that can be directed to the real server. When reached, sessions are sent to other real servers. + type: int monitor: description: - Name of the health check monitor to use when polling to determine a virtual server's connectivity status. Source firewall .ldb-monitor.name. + type: str port: description: - Port for communicating with the real server. Required if port forwarding is enabled. + type: int status: description: - Set the status of the real server to active so that it can accept traffic, or on standby or disabled so no traffic is sent. + type: str choices: - active - standby @@ -290,9 +347,11 @@ options: weight: description: - Weight of the real server. If weighted load balancing is enabled, the server with the highest weight gets more connections. - server-type: + type: int + server_type: description: - Protocol to be load balanced by the virtual server (also called the server load balance virtual IP). + type: str choices: - http - https @@ -306,45 +365,54 @@ options: service: description: - Service name. + type: list suboptions: name: description: - Service name. Source firewall.service.custom.name firewall.service.group.name. required: true - src-filter: + type: str + src_filter: description: - Source address filter. Each address must be either an IP/subnet (x.x.x.x/n) or a range (x.x.x.x-y.y.y.y). Separate addresses with spaces. + type: list suboptions: range: description: - Source-filter range. required: true - srcintf-filter: + type: str + srcintf_filter: description: - Interfaces to which the VIP applies. Separate the names with spaces. + type: list suboptions: - interface-name: + interface_name: description: - Interface name. Source system.interface.name. - required: true - ssl-algorithm: + type: str + ssl_algorithm: description: - Permitted encryption algorithms for SSL sessions according to encryption strength. + type: str choices: - high - medium - low - custom - ssl-certificate: + ssl_certificate: description: - The name of the SSL certificate to use for SSL acceleration. Source vpn.certificate.local.name. - ssl-cipher-suites: + type: str + ssl_cipher_suites: description: - SSL/TLS cipher suites acceptable from a client, ordered by priority. + type: list suboptions: cipher: description: - Cipher suite name. + type: str choices: - TLS-RSA-WITH-3DES-EDE-CBC-SHA - TLS-DHE-RSA-WITH-DES-CBC-SHA @@ -353,44 +421,52 @@ options: description: - SSL/TLS cipher suites priority. required: true + type: int versions: description: - SSL/TLS versions that the cipher suite can be used with. + type: str choices: - ssl-3.0 - tls-1.0 - tls-1.1 - tls-1.2 - ssl-client-fallback: + ssl_client_fallback: description: - Enable/disable support for preventing Downgrade Attacks on client connections (RFC 7507). + type: str choices: - disable - enable - ssl-client-renegotiation: + ssl_client_renegotiation: description: - Allow, deny, or require secure renegotiation of client sessions to comply with RFC 5746. + type: str choices: - allow - deny - secure - ssl-client-session-state-max: + ssl_client_session_state_max: description: - Maximum number of client to FortiGate SSL session states to keep. - ssl-client-session-state-timeout: + type: int + ssl_client_session_state_timeout: description: - Number of minutes to keep client to FortiGate SSL session state. - ssl-client-session-state-type: + type: int + ssl_client_session_state_type: description: - How to expire SSL sessions for the segment of the SSL connection between the client and the FortiGate. + type: str choices: - disable - time - count - both - ssl-dh-bits: + ssl_dh_bits: description: - Number of bits to use in the Diffie-Hellman exchange for RSA encryption of SSL sessions. + type: str choices: - 768 - 1024 @@ -398,111 +474,130 @@ options: - 2048 - 3072 - 4096 - ssl-hpkp: + ssl_hpkp: description: - Enable/disable including HPKP header in response. + type: str choices: - disable - enable - report-only - ssl-hpkp-age: + ssl_hpkp_age: description: - Number of seconds the client should honour the HPKP setting. - ssl-hpkp-backup: + type: int + ssl_hpkp_backup: description: - Certificate to generate backup HPKP pin from. Source vpn.certificate.local.name vpn.certificate.ca.name. - ssl-hpkp-include-subdomains: + type: str + ssl_hpkp_include_subdomains: description: - Indicate that HPKP header applies to all subdomains. + type: str choices: - disable - enable - ssl-hpkp-primary: + ssl_hpkp_primary: description: - Certificate to generate primary HPKP pin from. Source vpn.certificate.local.name vpn.certificate.ca.name. - ssl-hpkp-report-uri: + type: str + ssl_hpkp_report_uri: description: - URL to report HPKP violations to. - ssl-hsts: + type: str + ssl_hsts: description: - Enable/disable including HSTS header in response. + type: str choices: - disable - enable - ssl-hsts-age: + ssl_hsts_age: description: - Number of seconds the client should honour the HSTS setting. - ssl-hsts-include-subdomains: + type: int + ssl_hsts_include_subdomains: description: - Indicate that HSTS header applies to all subdomains. + type: str choices: - disable - enable - ssl-http-location-conversion: + ssl_http_location_conversion: description: - Enable to replace HTTP with HTTPS in the reply's Location HTTP header field. + type: str choices: - enable - disable - ssl-http-match-host: + ssl_http_match_host: description: - Enable/disable HTTP host matching for location conversion. + type: str choices: - enable - disable - ssl-max-version: + ssl_max_version: description: - Highest SSL/TLS version acceptable from a client. + type: str choices: - ssl-3.0 - tls-1.0 - tls-1.1 - tls-1.2 - ssl-min-version: + ssl_min_version: description: - Lowest SSL/TLS version acceptable from a client. + type: str choices: - ssl-3.0 - tls-1.0 - tls-1.1 - tls-1.2 - ssl-mode: + ssl_mode: description: - Apply SSL offloading between the client and the FortiGate (half) or from the client to the FortiGate and from the FortiGate to the server (full). + type: str choices: - half - full - ssl-pfs: + ssl_pfs: description: - Select the cipher suites that can be used for SSL perfect forward secrecy (PFS). Applies to both client and server sessions. + type: str choices: - require - deny - allow - ssl-send-empty-frags: + ssl_send_empty_frags: description: - Enable/disable sending empty fragments to avoid CBC IV attacks (SSL 3.0 & TLS 1.0 only). May need to be disabled for compatibility with older systems. + type: str choices: - enable - disable - ssl-server-algorithm: + ssl_server_algorithm: description: - Permitted encryption algorithms for the server side of SSL full mode sessions according to encryption strength. + type: str choices: - high - medium - low - custom - client - ssl-server-cipher-suites: + ssl_server_cipher_suites: description: - SSL/TLS cipher suites to offer to a server, ordered by priority. + type: list suboptions: cipher: description: - Cipher suite name. + type: str choices: - TLS-RSA-WITH-3DES-EDE-CBC-SHA - TLS-DHE-RSA-WITH-DES-CBC-SHA @@ -511,41 +606,48 @@ options: description: - SSL/TLS cipher suites priority. required: true + type: int versions: description: - SSL/TLS versions that the cipher suite can be used with. + type: str choices: - ssl-3.0 - tls-1.0 - tls-1.1 - tls-1.2 - ssl-server-max-version: + ssl_server_max_version: description: - Highest SSL/TLS version acceptable from a server. Use the client setting by default. + type: str choices: - ssl-3.0 - tls-1.0 - tls-1.1 - tls-1.2 - client - ssl-server-min-version: + ssl_server_min_version: description: - Lowest SSL/TLS version acceptable from a server. Use the client setting by default. + type: str choices: - ssl-3.0 - tls-1.0 - tls-1.1 - tls-1.2 - client - ssl-server-session-state-max: + ssl_server_session_state_max: description: - Maximum number of FortiGate to Server SSL session states to keep. - ssl-server-session-state-timeout: + type: int + ssl_server_session_state_timeout: description: - Number of minutes to keep FortiGate to Server SSL session state. - ssl-server-session-state-type: + type: int + ssl_server_session_state_type: description: - How to expire SSL sessions for the segment of the SSL connection between the server and the FortiGate. + type: str choices: - disable - time @@ -554,6 +656,7 @@ options: type: description: - Configure a static NAT, load balance, server load balance, DNS translation, or FQDN VIP. + type: str choices: - static-nat - load-balance @@ -563,15 +666,18 @@ options: uuid: description: - Universally Unique Identifier (UUID; automatically assigned but can be manually reset). - weblogic-server: + type: str + weblogic_server: description: - Enable to add an HTTP header to indicate SSL offloading for a WebLogic server. + type: str choices: - disable - enable - websphere-server: + websphere_server: description: - Enable to add an HTTP header to indicate SSL offloading for a WebSphere server. + type: str choices: - disable - enable @@ -584,6 +690,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure virtual IP for IPv4. fortios_firewall_vip: @@ -591,114 +698,115 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" + state: "present" firewall_vip: - state: "present" - arp-reply: "disable" + arp_reply: "disable" color: "4" comment: "Comment." - dns-mapping-ttl: "6" + dns_mapping_ttl: "6" extaddr: - name: "default_name_8 (source firewall.address.name firewall.addrgrp.name)" extintf: " (source system.interface.name)" extip: "" extport: "" - gratuitous-arp-interval: "12" - http-cookie-age: "13" - http-cookie-domain: "" - http-cookie-domain-from-host: "disable" - http-cookie-generation: "16" - http-cookie-path: "" - http-cookie-share: "disable" - http-ip-header: "enable" - http-ip-header-name: "" - http-multiplex: "enable" - https-cookie-secure: "disable" + gratuitous_arp_interval: "12" + http_cookie_age: "13" + http_cookie_domain: "" + http_cookie_domain_from_host: "disable" + http_cookie_generation: "16" + http_cookie_path: "" + http_cookie_share: "disable" + http_ip_header: "enable" + http_ip_header_name: "" + http_multiplex: "enable" + https_cookie_secure: "disable" id: "23" - ldb-method: "static" - mapped-addr: " (source firewall.address.name)" + ldb_method: "static" + mapped_addr: " (source firewall.address.name)" mappedip: - range: "" mappedport: "" - max-embryonic-connections: "29" + max_embryonic_connections: "29" monitor: - name: "default_name_31 (source firewall.ldb-monitor.name)" name: "default_name_32" - nat-source-vip: "disable" - outlook-web-access: "disable" + nat_source_vip: "disable" + outlook_web_access: "disable" persistence: "none" portforward: "disable" - portmapping-type: "1-to-1" + portmapping_type: "1-to-1" protocol: "tcp" realservers: - - client-ip: "" + client_ip: "" healthcheck: "disable" - holddown-interval: "42" - http-host: "myhostname" + holddown_interval: "42" + http_host: "myhostname" id: "44" ip: "" - max-connections: "46" + max_connections: "46" monitor: " (source firewall.ldb-monitor.name)" port: "48" status: "active" weight: "50" - server-type: "http" + server_type: "http" service: - name: "default_name_53 (source firewall.service.custom.name firewall.service.group.name)" - src-filter: + src_filter: - range: "" - srcintf-filter: + srcintf_filter: - - interface-name: " (source system.interface.name)" - ssl-algorithm: "high" - ssl-certificate: " (source vpn.certificate.local.name)" - ssl-cipher-suites: + interface_name: " (source system.interface.name)" + ssl_algorithm: "high" + ssl_certificate: " (source vpn.certificate.local.name)" + ssl_cipher_suites: - cipher: "TLS-RSA-WITH-3DES-EDE-CBC-SHA" priority: "62" versions: "ssl-3.0" - ssl-client-fallback: "disable" - ssl-client-renegotiation: "allow" - ssl-client-session-state-max: "66" - ssl-client-session-state-timeout: "67" - ssl-client-session-state-type: "disable" - ssl-dh-bits: "768" - ssl-hpkp: "disable" - ssl-hpkp-age: "71" - ssl-hpkp-backup: " (source vpn.certificate.local.name vpn.certificate.ca.name)" - ssl-hpkp-include-subdomains: "disable" - ssl-hpkp-primary: " (source vpn.certificate.local.name vpn.certificate.ca.name)" - ssl-hpkp-report-uri: "" - ssl-hsts: "disable" - ssl-hsts-age: "77" - ssl-hsts-include-subdomains: "disable" - ssl-http-location-conversion: "enable" - ssl-http-match-host: "enable" - ssl-max-version: "ssl-3.0" - ssl-min-version: "ssl-3.0" - ssl-mode: "half" - ssl-pfs: "require" - ssl-send-empty-frags: "enable" - ssl-server-algorithm: "high" - ssl-server-cipher-suites: + ssl_client_fallback: "disable" + ssl_client_renegotiation: "allow" + ssl_client_session_state_max: "66" + ssl_client_session_state_timeout: "67" + ssl_client_session_state_type: "disable" + ssl_dh_bits: "768" + ssl_hpkp: "disable" + ssl_hpkp_age: "71" + ssl_hpkp_backup: " (source vpn.certificate.local.name vpn.certificate.ca.name)" + ssl_hpkp_include_subdomains: "disable" + ssl_hpkp_primary: " (source vpn.certificate.local.name vpn.certificate.ca.name)" + ssl_hpkp_report_uri: "" + ssl_hsts: "disable" + ssl_hsts_age: "77" + ssl_hsts_include_subdomains: "disable" + ssl_http_location_conversion: "enable" + ssl_http_match_host: "enable" + ssl_max_version: "ssl-3.0" + ssl_min_version: "ssl-3.0" + ssl_mode: "half" + ssl_pfs: "require" + ssl_send_empty_frags: "enable" + ssl_server_algorithm: "high" + ssl_server_cipher_suites: - cipher: "TLS-RSA-WITH-3DES-EDE-CBC-SHA" priority: "89" versions: "ssl-3.0" - ssl-server-max-version: "ssl-3.0" - ssl-server-min-version: "ssl-3.0" - ssl-server-session-state-max: "93" - ssl-server-session-state-timeout: "94" - ssl-server-session-state-type: "disable" + ssl_server_max_version: "ssl-3.0" + ssl_server_min_version: "ssl-3.0" + ssl_server_session_state_max: "93" + ssl_server_session_state_timeout: "94" + ssl_server_session_state_type: "disable" type: "static-nat" uuid: "" - weblogic-server: "disable" - websphere-server: "disable" + weblogic_server: "disable" + websphere_server: "disable" ''' RETURN = ''' @@ -721,7 +829,7 @@ mkey: description: Master key (id) used in the last call to FortiGate returned: success type: str - sample: "key1" + sample: "id" name: description: Name of the table used to fulfill the request returned: always @@ -761,14 +869,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -776,35 +886,35 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_firewall_vip_data(json): - option_list = ['arp-reply', 'color', 'comment', - 'dns-mapping-ttl', 'extaddr', 'extintf', - 'extip', 'extport', 'gratuitous-arp-interval', - 'http-cookie-age', 'http-cookie-domain', 'http-cookie-domain-from-host', - 'http-cookie-generation', 'http-cookie-path', 'http-cookie-share', - 'http-ip-header', 'http-ip-header-name', 'http-multiplex', - 'https-cookie-secure', 'id', 'ldb-method', - 'mapped-addr', 'mappedip', 'mappedport', - 'max-embryonic-connections', 'monitor', 'name', - 'nat-source-vip', 'outlook-web-access', 'persistence', - 'portforward', 'portmapping-type', 'protocol', - 'realservers', 'server-type', 'service', - 'src-filter', 'srcintf-filter', 'ssl-algorithm', - 'ssl-certificate', 'ssl-cipher-suites', 'ssl-client-fallback', - 'ssl-client-renegotiation', 'ssl-client-session-state-max', 'ssl-client-session-state-timeout', - 'ssl-client-session-state-type', 'ssl-dh-bits', 'ssl-hpkp', - 'ssl-hpkp-age', 'ssl-hpkp-backup', 'ssl-hpkp-include-subdomains', - 'ssl-hpkp-primary', 'ssl-hpkp-report-uri', 'ssl-hsts', - 'ssl-hsts-age', 'ssl-hsts-include-subdomains', 'ssl-http-location-conversion', - 'ssl-http-match-host', 'ssl-max-version', 'ssl-min-version', - 'ssl-mode', 'ssl-pfs', 'ssl-send-empty-frags', - 'ssl-server-algorithm', 'ssl-server-cipher-suites', 'ssl-server-max-version', - 'ssl-server-min-version', 'ssl-server-session-state-max', 'ssl-server-session-state-timeout', - 'ssl-server-session-state-type', 'type', 'uuid', - 'weblogic-server', 'websphere-server'] + option_list = ['arp_reply', 'color', 'comment', + 'dns_mapping_ttl', 'extaddr', 'extintf', + 'extip', 'extport', 'gratuitous_arp_interval', + 'http_cookie_age', 'http_cookie_domain', 'http_cookie_domain_from_host', + 'http_cookie_generation', 'http_cookie_path', 'http_cookie_share', + 'http_ip_header', 'http_ip_header_name', 'http_multiplex', + 'https_cookie_secure', 'id', 'ldb_method', + 'mapped_addr', 'mappedip', 'mappedport', + 'max_embryonic_connections', 'monitor', 'name', + 'nat_source_vip', 'outlook_web_access', 'persistence', + 'portforward', 'portmapping_type', 'protocol', + 'realservers', 'server_type', 'service', + 'src_filter', 'srcintf_filter', 'ssl_algorithm', + 'ssl_certificate', 'ssl_cipher_suites', 'ssl_client_fallback', + 'ssl_client_renegotiation', 'ssl_client_session_state_max', 'ssl_client_session_state_timeout', + 'ssl_client_session_state_type', 'ssl_dh_bits', 'ssl_hpkp', + 'ssl_hpkp_age', 'ssl_hpkp_backup', 'ssl_hpkp_include_subdomains', + 'ssl_hpkp_primary', 'ssl_hpkp_report_uri', 'ssl_hsts', + 'ssl_hsts_age', 'ssl_hsts_include_subdomains', 'ssl_http_location_conversion', + 'ssl_http_match_host', 'ssl_max_version', 'ssl_min_version', + 'ssl_mode', 'ssl_pfs', 'ssl_send_empty_frags', + 'ssl_server_algorithm', 'ssl_server_cipher_suites', 'ssl_server_max_version', + 'ssl_server_min_version', 'ssl_server_session_state_max', 'ssl_server_session_state_timeout', + 'ssl_server_session_state_type', 'type', 'uuid', + 'weblogic_server', 'websphere_server'] dictionary = {} for attribute in option_list: @@ -814,53 +924,71 @@ def filter_firewall_vip_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def firewall_vip(data, fos): vdom = data['vdom'] + state = data['state'] firewall_vip_data = data['firewall_vip'] - filtered_data = filter_firewall_vip_data(firewall_vip_data) - if firewall_vip_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_firewall_vip_data(firewall_vip_data)) + + if state == "present": return fos.set('firewall', 'vip', data=filtered_data, vdom=vdom) - elif firewall_vip_data['state'] == "absent": + elif state == "absent": return fos.delete('firewall', 'vip', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_firewall(data, fos): - login(data) - methodlist = ['firewall_vip'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['firewall_vip']: + resp = firewall_vip(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, - "https": {"required": False, "type": "bool", "default": "False"}, + "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "firewall_vip": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, - "arp-reply": {"required": False, "type": "str", + "arp_reply": {"required": False, "type": "str", "choices": ["disable", "enable"]}, "color": {"required": False, "type": "int"}, "comment": {"required": False, "type": "str"}, - "dns-mapping-ttl": {"required": False, "type": "int"}, + "dns_mapping_ttl": {"required": False, "type": "int"}, "extaddr": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} @@ -868,69 +996,69 @@ def main(): "extintf": {"required": False, "type": "str"}, "extip": {"required": False, "type": "str"}, "extport": {"required": False, "type": "str"}, - "gratuitous-arp-interval": {"required": False, "type": "int"}, - "http-cookie-age": {"required": False, "type": "int"}, - "http-cookie-domain": {"required": False, "type": "str"}, - "http-cookie-domain-from-host": {"required": False, "type": "str", + "gratuitous_arp_interval": {"required": False, "type": "int"}, + "http_cookie_age": {"required": False, "type": "int"}, + "http_cookie_domain": {"required": False, "type": "str"}, + "http_cookie_domain_from_host": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "http-cookie-generation": {"required": False, "type": "int"}, - "http-cookie-path": {"required": False, "type": "str"}, - "http-cookie-share": {"required": False, "type": "str", + "http_cookie_generation": {"required": False, "type": "int"}, + "http_cookie_path": {"required": False, "type": "str"}, + "http_cookie_share": {"required": False, "type": "str", "choices": ["disable", "same-ip"]}, - "http-ip-header": {"required": False, "type": "str", + "http_ip_header": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "http-ip-header-name": {"required": False, "type": "str"}, - "http-multiplex": {"required": False, "type": "str", + "http_ip_header_name": {"required": False, "type": "str"}, + "http_multiplex": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "https-cookie-secure": {"required": False, "type": "str", + "https_cookie_secure": {"required": False, "type": "str", "choices": ["disable", "enable"]}, "id": {"required": False, "type": "int"}, - "ldb-method": {"required": False, "type": "str", + "ldb_method": {"required": False, "type": "str", "choices": ["static", "round-robin", "weighted", "least-session", "least-rtt", "first-alive", "http-host"]}, - "mapped-addr": {"required": False, "type": "str"}, + "mapped_addr": {"required": False, "type": "str"}, "mappedip": {"required": False, "type": "list", "options": { "range": {"required": True, "type": "str"} }}, "mappedport": {"required": False, "type": "str"}, - "max-embryonic-connections": {"required": False, "type": "int"}, + "max_embryonic_connections": {"required": False, "type": "int"}, "monitor": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, "name": {"required": True, "type": "str"}, - "nat-source-vip": {"required": False, "type": "str", + "nat_source_vip": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "outlook-web-access": {"required": False, "type": "str", + "outlook_web_access": {"required": False, "type": "str", "choices": ["disable", "enable"]}, "persistence": {"required": False, "type": "str", "choices": ["none", "http-cookie", "ssl-session-id"]}, "portforward": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "portmapping-type": {"required": False, "type": "str", + "portmapping_type": {"required": False, "type": "str", "choices": ["1-to-1", "m-to-n"]}, "protocol": {"required": False, "type": "str", "choices": ["tcp", "udp", "sctp", "icmp"]}, "realservers": {"required": False, "type": "list", "options": { - "client-ip": {"required": False, "type": "str"}, + "client_ip": {"required": False, "type": "str"}, "healthcheck": {"required": False, "type": "str", "choices": ["disable", "enable", "vip"]}, - "holddown-interval": {"required": False, "type": "int"}, - "http-host": {"required": False, "type": "str"}, + "holddown_interval": {"required": False, "type": "int"}, + "http_host": {"required": False, "type": "str"}, "id": {"required": True, "type": "int"}, "ip": {"required": False, "type": "str"}, - "max-connections": {"required": False, "type": "int"}, + "max_connections": {"required": False, "type": "int"}, "monitor": {"required": False, "type": "str"}, "port": {"required": False, "type": "int"}, "status": {"required": False, "type": "str", "choices": ["active", "standby", "disable"]}, "weight": {"required": False, "type": "int"} }}, - "server-type": {"required": False, "type": "str", + "server_type": {"required": False, "type": "str", "choices": ["http", "https", "imaps", "pop3s", "smtps", "ssl", "tcp", "udp", "ip"]}, @@ -938,102 +1066,100 @@ def main(): "options": { "name": {"required": True, "type": "str"} }}, - "src-filter": {"required": False, "type": "list", + "src_filter": {"required": False, "type": "list", "options": { "range": {"required": True, "type": "str"} }}, - "srcintf-filter": {"required": False, "type": "list", + "srcintf_filter": {"required": False, "type": "list", "options": { - "interface-name": {"required": True, "type": "str"} + "interface_name": {"required": False, "type": "str"} }}, - "ssl-algorithm": {"required": False, "type": "str", + "ssl_algorithm": {"required": False, "type": "str", "choices": ["high", "medium", "low", "custom"]}, - "ssl-certificate": {"required": False, "type": "str"}, - "ssl-cipher-suites": {"required": False, "type": "list", + "ssl_certificate": {"required": False, "type": "str"}, + "ssl_cipher_suites": {"required": False, "type": "list", "options": { "cipher": {"required": False, "type": "str", - "choices": ["TLS-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-RSA-WITH-DES-CBC-SHA", + "choices": ["TLS-RSA-WITH-3DES-EDE-CBC-SHA", "TLS-DHE-RSA-WITH-DES-CBC-SHA", "TLS-DHE-DSS-WITH-DES-CBC-SHA"]}, "priority": {"required": True, "type": "int"}, "versions": {"required": False, "type": "str", "choices": ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]} }}, - "ssl-client-fallback": {"required": False, "type": "str", + "ssl_client_fallback": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "ssl-client-renegotiation": {"required": False, "type": "str", + "ssl_client_renegotiation": {"required": False, "type": "str", "choices": ["allow", "deny", "secure"]}, - "ssl-client-session-state-max": {"required": False, "type": "int"}, - "ssl-client-session-state-timeout": {"required": False, "type": "int"}, - "ssl-client-session-state-type": {"required": False, "type": "str", + "ssl_client_session_state_max": {"required": False, "type": "int"}, + "ssl_client_session_state_timeout": {"required": False, "type": "int"}, + "ssl_client_session_state_type": {"required": False, "type": "str", "choices": ["disable", "time", "count", "both"]}, - "ssl-dh-bits": {"required": False, "type": "str", + "ssl_dh_bits": {"required": False, "type": "str", "choices": ["768", "1024", "1536", "2048", "3072", "4096"]}, - "ssl-hpkp": {"required": False, "type": "str", + "ssl_hpkp": {"required": False, "type": "str", "choices": ["disable", "enable", "report-only"]}, - "ssl-hpkp-age": {"required": False, "type": "int"}, - "ssl-hpkp-backup": {"required": False, "type": "str"}, - "ssl-hpkp-include-subdomains": {"required": False, "type": "str", + "ssl_hpkp_age": {"required": False, "type": "int"}, + "ssl_hpkp_backup": {"required": False, "type": "str"}, + "ssl_hpkp_include_subdomains": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "ssl-hpkp-primary": {"required": False, "type": "str"}, - "ssl-hpkp-report-uri": {"required": False, "type": "str"}, - "ssl-hsts": {"required": False, "type": "str", + "ssl_hpkp_primary": {"required": False, "type": "str"}, + "ssl_hpkp_report_uri": {"required": False, "type": "str"}, + "ssl_hsts": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "ssl-hsts-age": {"required": False, "type": "int"}, - "ssl-hsts-include-subdomains": {"required": False, "type": "str", + "ssl_hsts_age": {"required": False, "type": "int"}, + "ssl_hsts_include_subdomains": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "ssl-http-location-conversion": {"required": False, "type": "str", + "ssl_http_location_conversion": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ssl-http-match-host": {"required": False, "type": "str", + "ssl_http_match_host": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ssl-max-version": {"required": False, "type": "str", + "ssl_max_version": {"required": False, "type": "str", "choices": ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]}, - "ssl-min-version": {"required": False, "type": "str", + "ssl_min_version": {"required": False, "type": "str", "choices": ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]}, - "ssl-mode": {"required": False, "type": "str", + "ssl_mode": {"required": False, "type": "str", "choices": ["half", "full"]}, - "ssl-pfs": {"required": False, "type": "str", + "ssl_pfs": {"required": False, "type": "str", "choices": ["require", "deny", "allow"]}, - "ssl-send-empty-frags": {"required": False, "type": "str", + "ssl_send_empty_frags": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ssl-server-algorithm": {"required": False, "type": "str", + "ssl_server_algorithm": {"required": False, "type": "str", "choices": ["high", "medium", "low", "custom", "client"]}, - "ssl-server-cipher-suites": {"required": False, "type": "list", + "ssl_server_cipher_suites": {"required": False, "type": "list", "options": { "cipher": {"required": False, "type": "str", - "choices": ["TLS-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-RSA-WITH-DES-CBC-SHA", + "choices": ["TLS-RSA-WITH-3DES-EDE-CBC-SHA", "TLS-DHE-RSA-WITH-DES-CBC-SHA", "TLS-DHE-DSS-WITH-DES-CBC-SHA"]}, "priority": {"required": True, "type": "int"}, "versions": {"required": False, "type": "str", "choices": ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]} }}, - "ssl-server-max-version": {"required": False, "type": "str", + "ssl_server_max_version": {"required": False, "type": "str", "choices": ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2", "client"]}, - "ssl-server-min-version": {"required": False, "type": "str", + "ssl_server_min_version": {"required": False, "type": "str", "choices": ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2", "client"]}, - "ssl-server-session-state-max": {"required": False, "type": "int"}, - "ssl-server-session-state-timeout": {"required": False, "type": "int"}, - "ssl-server-session-state-type": {"required": False, "type": "str", + "ssl_server_session_state_max": {"required": False, "type": "int"}, + "ssl_server_session_state_timeout": {"required": False, "type": "int"}, + "ssl_server_session_state_type": {"required": False, "type": "str", "choices": ["disable", "time", "count", "both"]}, "type": {"required": False, "type": "str", "choices": ["static-nat", "load-balance", "server-load-balance", "dns-translation", "fqdn"]}, "uuid": {"required": False, "type": "str"}, - "weblogic-server": {"required": False, "type": "str", + "weblogic_server": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "websphere-server": {"required": False, "type": "str", + "websphere_server": {"required": False, "type": "str", "choices": ["disable", "enable"]} } @@ -1042,15 +1168,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_firewall(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_firewall(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_firewall(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_firewall_vip46.py b/lib/ansible/modules/network/fortios/fortios_firewall_vip46.py index 1409e9a79e0..90f70a0c892 100644 --- a/lib/ansible/modules/network/fortios/fortios_firewall_vip46.py +++ b/lib/ansible/modules/network/fortios/fortios_firewall_vip46.py @@ -1,6 +1,6 @@ #!/usr/bin/python from __future__ import (absolute_import, division, print_function) -# Copyright 2018 Fortinet, Inc. +# Copyright 2019 Fortinet, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_firewall_vip46 short_description: Configure IPv4 to IPv6 virtual IPs in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure firewall feature and vip46 category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify firewall feature and vip46 category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,64 +41,84 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool - default: false + default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 firewall_vip46: description: - Configure IPv4 to IPv6 virtual IPs. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent - arp-reply: + arp_reply: description: - Enable ARP reply. + type: str choices: - disable - enable color: description: - Color of icon on the GUI. + type: int comment: description: - Comment. + type: str extip: description: - Start-external-IP [-end-external-IP]. + type: str extport: description: - External service port. + type: str id: description: - Custom defined id. - ldb-method: + type: int + ldb_method: description: - Load balance method. + type: str choices: - static - round-robin @@ -112,69 +129,86 @@ options: mappedip: description: - Start-mapped-IP [-end mapped-IP]. + type: str mappedport: description: - Mapped service port. + type: str monitor: description: - Health monitors. + type: list suboptions: name: description: - Health monitor name. Source firewall.ldb-monitor.name. required: true + type: str name: description: - VIP46 name. required: true + type: str portforward: description: - Enable port forwarding. + type: str choices: - disable - enable protocol: description: - Mapped port protocol. + type: str choices: - tcp - udp realservers: description: - Real servers. + type: list suboptions: - client-ip: + client_ip: description: - Restrict server to a client IP in this range. + type: str healthcheck: description: - Per server health check. + type: str choices: - disable - enable - vip - holddown-interval: + holddown_interval: description: - Hold down interval. + type: int id: description: - Real server ID. required: true + type: int ip: description: - Mapped server IPv6. - max-connections: + type: str + max_connections: description: - Maximum number of connections allowed to server. + type: int monitor: description: - Health monitors. Source firewall.ldb-monitor.name. + type: str port: description: - Mapped server port. + type: int status: description: - Server administrative status. + type: str choices: - active - standby @@ -182,31 +216,37 @@ options: weight: description: - weight - server-type: + type: int + server_type: description: - Server type. + type: str choices: - http - tcp - udp - ip - src-filter: + src_filter: description: - Source IP filter (x.x.x.x/x). + type: list suboptions: range: description: - Src-filter range. required: true + type: str type: description: - "VIP type: static NAT or server load balance." + type: str choices: - static-nat - server-load-balance uuid: description: - Universally Unique Identifier (UUID; automatically assigned but can be manually reset). + type: str ''' EXAMPLES = ''' @@ -216,6 +256,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPv4 to IPv6 virtual IPs. fortios_firewall_vip46: @@ -223,15 +264,16 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" + state: "present" firewall_vip46: - state: "present" - arp-reply: "disable" + arp_reply: "disable" color: "4" comment: "Comment." extip: "" extport: "" id: "8" - ldb-method: "static" + ldb_method: "static" mappedip: "" mappedport: "" monitor: @@ -242,18 +284,18 @@ EXAMPLES = ''' protocol: "tcp" realservers: - - client-ip: "" + client_ip: "" healthcheck: "disable" - holddown-interval: "20" + holddown_interval: "20" id: "21" ip: "" - max-connections: "23" + max_connections: "23" monitor: " (source firewall.ldb-monitor.name)" port: "25" status: "active" weight: "27" - server-type: "http" - src-filter: + server_type: "http" + src_filter: - range: "" type: "static-nat" @@ -280,7 +322,7 @@ mkey: description: Master key (id) used in the last call to FortiGate returned: success type: str - sample: "key1" + sample: "id" name: description: Name of the table used to fulfill the request returned: always @@ -320,14 +362,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -335,16 +379,16 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_firewall_vip46_data(json): - option_list = ['arp-reply', 'color', 'comment', + option_list = ['arp_reply', 'color', 'comment', 'extip', 'extport', 'id', - 'ldb-method', 'mappedip', 'mappedport', + 'ldb_method', 'mappedip', 'mappedport', 'monitor', 'name', 'portforward', - 'protocol', 'realservers', 'server-type', - 'src-filter', 'type', 'uuid'] + 'protocol', 'realservers', 'server_type', + 'src_filter', 'type', 'uuid'] dictionary = {} for attribute in option_list: @@ -354,56 +398,74 @@ def filter_firewall_vip46_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def firewall_vip46(data, fos): vdom = data['vdom'] + state = data['state'] firewall_vip46_data = data['firewall_vip46'] - filtered_data = filter_firewall_vip46_data(firewall_vip46_data) - if firewall_vip46_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_firewall_vip46_data(firewall_vip46_data)) + + if state == "present": return fos.set('firewall', 'vip46', data=filtered_data, vdom=vdom) - elif firewall_vip46_data['state'] == "absent": + elif state == "absent": return fos.delete('firewall', 'vip46', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_firewall(data, fos): - login(data) - methodlist = ['firewall_vip46'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['firewall_vip46']: + resp = firewall_vip46(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, - "https": {"required": False, "type": "bool", "default": "False"}, + "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "firewall_vip46": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, - "arp-reply": {"required": False, "type": "str", + "arp_reply": {"required": False, "type": "str", "choices": ["disable", "enable"]}, "color": {"required": False, "type": "int"}, "comment": {"required": False, "type": "str"}, "extip": {"required": False, "type": "str"}, "extport": {"required": False, "type": "str"}, "id": {"required": False, "type": "int"}, - "ldb-method": {"required": False, "type": "str", + "ldb_method": {"required": False, "type": "str", "choices": ["static", "round-robin", "weighted", "least-session", "least-rtt", "first-alive"]}, "mappedip": {"required": False, "type": "str"}, @@ -419,23 +481,23 @@ def main(): "choices": ["tcp", "udp"]}, "realservers": {"required": False, "type": "list", "options": { - "client-ip": {"required": False, "type": "str"}, + "client_ip": {"required": False, "type": "str"}, "healthcheck": {"required": False, "type": "str", "choices": ["disable", "enable", "vip"]}, - "holddown-interval": {"required": False, "type": "int"}, + "holddown_interval": {"required": False, "type": "int"}, "id": {"required": True, "type": "int"}, "ip": {"required": False, "type": "str"}, - "max-connections": {"required": False, "type": "int"}, + "max_connections": {"required": False, "type": "int"}, "monitor": {"required": False, "type": "str"}, "port": {"required": False, "type": "int"}, "status": {"required": False, "type": "str", "choices": ["active", "standby", "disable"]}, "weight": {"required": False, "type": "int"} }}, - "server-type": {"required": False, "type": "str", + "server_type": {"required": False, "type": "str", "choices": ["http", "tcp", "udp", "ip"]}, - "src-filter": {"required": False, "type": "list", + "src_filter": {"required": False, "type": "list", "options": { "range": {"required": True, "type": "str"} }}, @@ -449,15 +511,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_firewall(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_firewall(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_firewall(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_firewall_vip6.py b/lib/ansible/modules/network/fortios/fortios_firewall_vip6.py index 07972642e45..f5757806fe8 100644 --- a/lib/ansible/modules/network/fortios/fortios_firewall_vip6.py +++ b/lib/ansible/modules/network/fortios/fortios_firewall_vip6.py @@ -1,6 +1,6 @@ #!/usr/bin/python from __future__ import (absolute_import, division, print_function) -# Copyright 2018 Fortinet, Inc. +# Copyright 2019 Fortinet, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_firewall_vip6 short_description: Configure virtual IP for IPv6 in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure firewall feature and vip6 category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify firewall feature and vip6 category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,111 +41,141 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool - default: false + default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 firewall_vip6: description: - Configure virtual IP for IPv6. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent - arp-reply: + arp_reply: description: - Enable to respond to ARP requests for this virtual IP address. Enabled by default. + type: str choices: - disable - enable color: description: - Color of icon on the GUI. + type: int comment: description: - Comment. + type: str extip: description: - IP address or address range on the external interface that you want to map to an address or address range on the destination network. + type: str extport: description: - Incoming port number range that you want to map to a port number range on the destination network. - http-cookie-age: + type: str + http_cookie_age: description: - Time in minutes that client web browsers should keep a cookie. Default is 60 seconds. 0 = no time limit. - http-cookie-domain: + type: int + http_cookie_domain: description: - Domain that HTTP cookie persistence should apply to. - http-cookie-domain-from-host: + type: str + http_cookie_domain_from_host: description: - Enable/disable use of HTTP cookie domain from host field in HTTP. + type: str choices: - disable - enable - http-cookie-generation: + http_cookie_generation: description: - Generation of HTTP cookie to be accepted. Changing invalidates all existing cookies. - http-cookie-path: + type: int + http_cookie_path: description: - Limit HTTP cookie persistence to the specified path. - http-cookie-share: + type: str + http_cookie_share: description: - Control sharing of cookies across virtual servers. same-ip means a cookie from one virtual server can be used by another. Disable stops cookie sharing. + type: str choices: - disable - same-ip - http-ip-header: + http_ip_header: description: - For HTTP multiplexing, enable to add the original client IP address in the XForwarded-For HTTP header. + type: str choices: - enable - disable - http-ip-header-name: + http_ip_header_name: description: - For HTTP multiplexing, enter a custom HTTPS header name. The original client IP address is added to this header. If empty, X-Forwarded-For is used. - http-multiplex: + type: str + http_multiplex: description: - Enable/disable HTTP multiplexing. + type: str choices: - enable - disable - https-cookie-secure: + https_cookie_secure: description: - Enable/disable verification that inserted HTTPS cookies are secure. + type: str choices: - disable - enable id: description: - Custom defined ID. - ldb-method: + type: int + ldb_method: description: - Method used to distribute sessions to real servers. + type: str choices: - static - round-robin @@ -160,33 +187,41 @@ options: mappedip: description: - Mapped IP address range in the format startIP-endIP. + type: str mappedport: description: - Port number range on the destination network to which the external port number range is mapped. - max-embryonic-connections: + type: str + max_embryonic_connections: description: - Maximum number of incomplete connections. + type: int monitor: description: - Name of the health check monitor to use when polling to determine a virtual server's connectivity status. + type: list suboptions: name: description: - Health monitor name. Source firewall.ldb-monitor.name. required: true + type: str name: description: - Virtual ip6 name. required: true - outlook-web-access: + type: str + outlook_web_access: description: - Enable to add the Front-End-Https header for Microsoft Outlook Web Access. + type: str choices: - disable - enable persistence: description: - Configure how to make sure that clients connect to the same server every time they make a request that is part of the same session. + type: str choices: - none - http-cookie @@ -194,12 +229,14 @@ options: portforward: description: - Enable port forwarding. + type: str choices: - disable - enable protocol: description: - Protocol to use when forwarding packets. + type: str choices: - tcp - udp @@ -207,43 +244,54 @@ options: realservers: description: - Select the real servers that this server load balancing VIP will distribute traffic to. + type: list suboptions: - client-ip: + client_ip: description: - Only clients in this IP range can connect to this real server. + type: str healthcheck: description: - Enable to check the responsiveness of the real server before forwarding traffic. + type: str choices: - disable - enable - vip - holddown-interval: + holddown_interval: description: - Time in seconds that the health check monitor continues to monitor an unresponsive server that should be active. - http-host: + type: int + http_host: description: - HTTP server domain name in HTTP header. + type: str id: description: - Real server ID. required: true + type: int ip: description: - IPv6 address of the real server. - max-connections: + type: str + max_connections: description: - Max number of active connections that can directed to the real server. When reached, sessions are sent to other real servers. + type: int monitor: description: - Name of the health check monitor to use when polling to determine a virtual server's connectivity status. Source firewall .ldb-monitor.name. + type: str port: description: - Port for communicating with the real server. Required if port forwarding is enabled. + type: int status: description: - Set the status of the real server to active so that it can accept traffic, or on standby or disabled so no traffic is sent. + type: str choices: - active - standby @@ -251,9 +299,11 @@ options: weight: description: - Weight of the real server. If weighted load balancing is enabled, the server with the highest weight gets more connections. - server-type: + type: int + server_type: description: - Protocol to be load balanced by the virtual server (also called the server load balance virtual IP). + type: str choices: - http - https @@ -264,32 +314,38 @@ options: - tcp - udp - ip - src-filter: + src_filter: description: - "Source IP6 filter (x:x:x:x:x:x:x:x/x). Separate addresses with spaces." + type: list suboptions: range: description: - Source-filter range. required: true - ssl-algorithm: + type: str + ssl_algorithm: description: - Permitted encryption algorithms for SSL sessions according to encryption strength. + type: str choices: - high - medium - low - custom - ssl-certificate: + ssl_certificate: description: - The name of the SSL certificate to use for SSL acceleration. Source vpn.certificate.local.name. - ssl-cipher-suites: + type: str + ssl_cipher_suites: description: - SSL/TLS cipher suites acceptable from a client, ordered by priority. + type: list suboptions: cipher: description: - Cipher suite name. + type: str choices: - TLS-RSA-WITH-3DES-EDE-CBC-SHA - TLS-DHE-RSA-WITH-DES-CBC-SHA @@ -298,44 +354,52 @@ options: description: - SSL/TLS cipher suites priority. required: true + type: int versions: description: - SSL/TLS versions that the cipher suite can be used with. + type: str choices: - ssl-3.0 - tls-1.0 - tls-1.1 - tls-1.2 - ssl-client-fallback: + ssl_client_fallback: description: - Enable/disable support for preventing Downgrade Attacks on client connections (RFC 7507). + type: str choices: - disable - enable - ssl-client-renegotiation: + ssl_client_renegotiation: description: - Allow, deny, or require secure renegotiation of client sessions to comply with RFC 5746. + type: str choices: - allow - deny - secure - ssl-client-session-state-max: + ssl_client_session_state_max: description: - Maximum number of client to FortiGate SSL session states to keep. - ssl-client-session-state-timeout: + type: int + ssl_client_session_state_timeout: description: - Number of minutes to keep client to FortiGate SSL session state. - ssl-client-session-state-type: + type: int + ssl_client_session_state_type: description: - How to expire SSL sessions for the segment of the SSL connection between the client and the FortiGate. + type: str choices: - disable - time - count - both - ssl-dh-bits: + ssl_dh_bits: description: - Number of bits to use in the Diffie-Hellman exchange for RSA encryption of SSL sessions. + type: str choices: - 768 - 1024 @@ -343,111 +407,130 @@ options: - 2048 - 3072 - 4096 - ssl-hpkp: + ssl_hpkp: description: - Enable/disable including HPKP header in response. + type: str choices: - disable - enable - report-only - ssl-hpkp-age: + ssl_hpkp_age: description: - Number of minutes the web browser should keep HPKP. - ssl-hpkp-backup: + type: int + ssl_hpkp_backup: description: - Certificate to generate backup HPKP pin from. Source vpn.certificate.local.name vpn.certificate.ca.name. - ssl-hpkp-include-subdomains: + type: str + ssl_hpkp_include_subdomains: description: - Indicate that HPKP header applies to all subdomains. + type: str choices: - disable - enable - ssl-hpkp-primary: + ssl_hpkp_primary: description: - Certificate to generate primary HPKP pin from. Source vpn.certificate.local.name vpn.certificate.ca.name. - ssl-hpkp-report-uri: + type: str + ssl_hpkp_report_uri: description: - URL to report HPKP violations to. - ssl-hsts: + type: str + ssl_hsts: description: - Enable/disable including HSTS header in response. + type: str choices: - disable - enable - ssl-hsts-age: + ssl_hsts_age: description: - Number of seconds the client should honour the HSTS setting. - ssl-hsts-include-subdomains: + type: int + ssl_hsts_include_subdomains: description: - Indicate that HSTS header applies to all subdomains. + type: str choices: - disable - enable - ssl-http-location-conversion: + ssl_http_location_conversion: description: - Enable to replace HTTP with HTTPS in the reply's Location HTTP header field. + type: str choices: - enable - disable - ssl-http-match-host: + ssl_http_match_host: description: - Enable/disable HTTP host matching for location conversion. + type: str choices: - enable - disable - ssl-max-version: + ssl_max_version: description: - Highest SSL/TLS version acceptable from a client. + type: str choices: - ssl-3.0 - tls-1.0 - tls-1.1 - tls-1.2 - ssl-min-version: + ssl_min_version: description: - Lowest SSL/TLS version acceptable from a client. + type: str choices: - ssl-3.0 - tls-1.0 - tls-1.1 - tls-1.2 - ssl-mode: + ssl_mode: description: - Apply SSL offloading between the client and the FortiGate (half) or from the client to the FortiGate and from the FortiGate to the server (full). + type: str choices: - half - full - ssl-pfs: + ssl_pfs: description: - Select the cipher suites that can be used for SSL perfect forward secrecy (PFS). Applies to both client and server sessions. + type: str choices: - require - deny - allow - ssl-send-empty-frags: + ssl_send_empty_frags: description: - Enable/disable sending empty fragments to avoid CBC IV attacks (SSL 3.0 & TLS 1.0 only). May need to be disabled for compatibility with older systems. + type: str choices: - enable - disable - ssl-server-algorithm: + ssl_server_algorithm: description: - Permitted encryption algorithms for the server side of SSL full mode sessions according to encryption strength. + type: str choices: - high - medium - low - custom - client - ssl-server-cipher-suites: + ssl_server_cipher_suites: description: - SSL/TLS cipher suites to offer to a server, ordered by priority. + type: list suboptions: cipher: description: - Cipher suite name. + type: str choices: - TLS-RSA-WITH-3DES-EDE-CBC-SHA - TLS-DHE-RSA-WITH-DES-CBC-SHA @@ -456,41 +539,48 @@ options: description: - SSL/TLS cipher suites priority. required: true + type: int versions: description: - SSL/TLS versions that the cipher suite can be used with. + type: str choices: - ssl-3.0 - tls-1.0 - tls-1.1 - tls-1.2 - ssl-server-max-version: + ssl_server_max_version: description: - Highest SSL/TLS version acceptable from a server. Use the client setting by default. + type: str choices: - ssl-3.0 - tls-1.0 - tls-1.1 - tls-1.2 - client - ssl-server-min-version: + ssl_server_min_version: description: - Lowest SSL/TLS version acceptable from a server. Use the client setting by default. + type: str choices: - ssl-3.0 - tls-1.0 - tls-1.1 - tls-1.2 - client - ssl-server-session-state-max: + ssl_server_session_state_max: description: - Maximum number of FortiGate to Server SSL session states to keep. - ssl-server-session-state-timeout: + type: int + ssl_server_session_state_timeout: description: - Number of minutes to keep FortiGate to Server SSL session state. - ssl-server-session-state-type: + type: int + ssl_server_session_state_type: description: - How to expire SSL sessions for the segment of the SSL connection between the server and the FortiGate. + type: str choices: - disable - time @@ -499,21 +589,25 @@ options: type: description: - Configure a static NAT or server load balance VIP. + type: str choices: - static-nat - server-load-balance uuid: description: - Universally Unique Identifier (UUID; automatically assigned but can be manually reset). - weblogic-server: + type: str + weblogic_server: description: - Enable to add an HTTP header to indicate SSL offloading for a WebLogic server. + type: str choices: - disable - enable - websphere-server: + websphere_server: description: - Enable to add an HTTP header to indicate SSL offloading for a WebSphere server. + type: str choices: - disable - enable @@ -526,6 +620,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure virtual IP for IPv6. fortios_firewall_vip6: @@ -533,97 +628,98 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" + state: "present" firewall_vip6: - state: "present" - arp-reply: "disable" + arp_reply: "disable" color: "4" comment: "Comment." extip: "" extport: "" - http-cookie-age: "8" - http-cookie-domain: "" - http-cookie-domain-from-host: "disable" - http-cookie-generation: "11" - http-cookie-path: "" - http-cookie-share: "disable" - http-ip-header: "enable" - http-ip-header-name: "" - http-multiplex: "enable" - https-cookie-secure: "disable" + http_cookie_age: "8" + http_cookie_domain: "" + http_cookie_domain_from_host: "disable" + http_cookie_generation: "11" + http_cookie_path: "" + http_cookie_share: "disable" + http_ip_header: "enable" + http_ip_header_name: "" + http_multiplex: "enable" + https_cookie_secure: "disable" id: "18" - ldb-method: "static" + ldb_method: "static" mappedip: "" mappedport: "" - max-embryonic-connections: "22" + max_embryonic_connections: "22" monitor: - name: "default_name_24 (source firewall.ldb-monitor.name)" name: "default_name_25" - outlook-web-access: "disable" + outlook_web_access: "disable" persistence: "none" portforward: "disable" protocol: "tcp" realservers: - - client-ip: "" + client_ip: "" healthcheck: "disable" - holddown-interval: "33" - http-host: "myhostname" + holddown_interval: "33" + http_host: "myhostname" id: "35" ip: "" - max-connections: "37" + max_connections: "37" monitor: " (source firewall.ldb-monitor.name)" port: "39" status: "active" weight: "41" - server-type: "http" - src-filter: + server_type: "http" + src_filter: - range: "" - ssl-algorithm: "high" - ssl-certificate: " (source vpn.certificate.local.name)" - ssl-cipher-suites: + ssl_algorithm: "high" + ssl_certificate: " (source vpn.certificate.local.name)" + ssl_cipher_suites: - cipher: "TLS-RSA-WITH-3DES-EDE-CBC-SHA" priority: "49" versions: "ssl-3.0" - ssl-client-fallback: "disable" - ssl-client-renegotiation: "allow" - ssl-client-session-state-max: "53" - ssl-client-session-state-timeout: "54" - ssl-client-session-state-type: "disable" - ssl-dh-bits: "768" - ssl-hpkp: "disable" - ssl-hpkp-age: "58" - ssl-hpkp-backup: " (source vpn.certificate.local.name vpn.certificate.ca.name)" - ssl-hpkp-include-subdomains: "disable" - ssl-hpkp-primary: " (source vpn.certificate.local.name vpn.certificate.ca.name)" - ssl-hpkp-report-uri: "" - ssl-hsts: "disable" - ssl-hsts-age: "64" - ssl-hsts-include-subdomains: "disable" - ssl-http-location-conversion: "enable" - ssl-http-match-host: "enable" - ssl-max-version: "ssl-3.0" - ssl-min-version: "ssl-3.0" - ssl-mode: "half" - ssl-pfs: "require" - ssl-send-empty-frags: "enable" - ssl-server-algorithm: "high" - ssl-server-cipher-suites: + ssl_client_fallback: "disable" + ssl_client_renegotiation: "allow" + ssl_client_session_state_max: "53" + ssl_client_session_state_timeout: "54" + ssl_client_session_state_type: "disable" + ssl_dh_bits: "768" + ssl_hpkp: "disable" + ssl_hpkp_age: "58" + ssl_hpkp_backup: " (source vpn.certificate.local.name vpn.certificate.ca.name)" + ssl_hpkp_include_subdomains: "disable" + ssl_hpkp_primary: " (source vpn.certificate.local.name vpn.certificate.ca.name)" + ssl_hpkp_report_uri: "" + ssl_hsts: "disable" + ssl_hsts_age: "64" + ssl_hsts_include_subdomains: "disable" + ssl_http_location_conversion: "enable" + ssl_http_match_host: "enable" + ssl_max_version: "ssl-3.0" + ssl_min_version: "ssl-3.0" + ssl_mode: "half" + ssl_pfs: "require" + ssl_send_empty_frags: "enable" + ssl_server_algorithm: "high" + ssl_server_cipher_suites: - cipher: "TLS-RSA-WITH-3DES-EDE-CBC-SHA" priority: "76" versions: "ssl-3.0" - ssl-server-max-version: "ssl-3.0" - ssl-server-min-version: "ssl-3.0" - ssl-server-session-state-max: "80" - ssl-server-session-state-timeout: "81" - ssl-server-session-state-type: "disable" + ssl_server_max_version: "ssl-3.0" + ssl_server_min_version: "ssl-3.0" + ssl_server_session_state_max: "80" + ssl_server_session_state_timeout: "81" + ssl_server_session_state_type: "disable" type: "static-nat" uuid: "" - weblogic-server: "disable" - websphere-server: "disable" + weblogic_server: "disable" + websphere_server: "disable" ''' RETURN = ''' @@ -646,7 +742,7 @@ mkey: description: Master key (id) used in the last call to FortiGate returned: success type: str - sample: "key1" + sample: "id" name: description: Name of the table used to fulfill the request returned: always @@ -686,14 +782,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -701,32 +799,32 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_firewall_vip6_data(json): - option_list = ['arp-reply', 'color', 'comment', - 'extip', 'extport', 'http-cookie-age', - 'http-cookie-domain', 'http-cookie-domain-from-host', 'http-cookie-generation', - 'http-cookie-path', 'http-cookie-share', 'http-ip-header', - 'http-ip-header-name', 'http-multiplex', 'https-cookie-secure', - 'id', 'ldb-method', 'mappedip', - 'mappedport', 'max-embryonic-connections', 'monitor', - 'name', 'outlook-web-access', 'persistence', + option_list = ['arp_reply', 'color', 'comment', + 'extip', 'extport', 'http_cookie_age', + 'http_cookie_domain', 'http_cookie_domain_from_host', 'http_cookie_generation', + 'http_cookie_path', 'http_cookie_share', 'http_ip_header', + 'http_ip_header_name', 'http_multiplex', 'https_cookie_secure', + 'id', 'ldb_method', 'mappedip', + 'mappedport', 'max_embryonic_connections', 'monitor', + 'name', 'outlook_web_access', 'persistence', 'portforward', 'protocol', 'realservers', - 'server-type', 'src-filter', 'ssl-algorithm', - 'ssl-certificate', 'ssl-cipher-suites', 'ssl-client-fallback', - 'ssl-client-renegotiation', 'ssl-client-session-state-max', 'ssl-client-session-state-timeout', - 'ssl-client-session-state-type', 'ssl-dh-bits', 'ssl-hpkp', - 'ssl-hpkp-age', 'ssl-hpkp-backup', 'ssl-hpkp-include-subdomains', - 'ssl-hpkp-primary', 'ssl-hpkp-report-uri', 'ssl-hsts', - 'ssl-hsts-age', 'ssl-hsts-include-subdomains', 'ssl-http-location-conversion', - 'ssl-http-match-host', 'ssl-max-version', 'ssl-min-version', - 'ssl-mode', 'ssl-pfs', 'ssl-send-empty-frags', - 'ssl-server-algorithm', 'ssl-server-cipher-suites', 'ssl-server-max-version', - 'ssl-server-min-version', 'ssl-server-session-state-max', 'ssl-server-session-state-timeout', - 'ssl-server-session-state-type', 'type', 'uuid', - 'weblogic-server', 'websphere-server'] + 'server_type', 'src_filter', 'ssl_algorithm', + 'ssl_certificate', 'ssl_cipher_suites', 'ssl_client_fallback', + 'ssl_client_renegotiation', 'ssl_client_session_state_max', 'ssl_client_session_state_timeout', + 'ssl_client_session_state_type', 'ssl_dh_bits', 'ssl_hpkp', + 'ssl_hpkp_age', 'ssl_hpkp_backup', 'ssl_hpkp_include_subdomains', + 'ssl_hpkp_primary', 'ssl_hpkp_report_uri', 'ssl_hsts', + 'ssl_hsts_age', 'ssl_hsts_include_subdomains', 'ssl_http_location_conversion', + 'ssl_http_match_host', 'ssl_max_version', 'ssl_min_version', + 'ssl_mode', 'ssl_pfs', 'ssl_send_empty_frags', + 'ssl_server_algorithm', 'ssl_server_cipher_suites', 'ssl_server_max_version', + 'ssl_server_min_version', 'ssl_server_session_state_max', 'ssl_server_session_state_timeout', + 'ssl_server_session_state_type', 'type', 'uuid', + 'weblogic_server', 'websphere_server'] dictionary = {} for attribute in option_list: @@ -736,83 +834,101 @@ def filter_firewall_vip6_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def firewall_vip6(data, fos): vdom = data['vdom'] + state = data['state'] firewall_vip6_data = data['firewall_vip6'] - filtered_data = filter_firewall_vip6_data(firewall_vip6_data) - if firewall_vip6_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_firewall_vip6_data(firewall_vip6_data)) + + if state == "present": return fos.set('firewall', 'vip6', data=filtered_data, vdom=vdom) - elif firewall_vip6_data['state'] == "absent": + elif state == "absent": return fos.delete('firewall', 'vip6', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_firewall(data, fos): - login(data) - methodlist = ['firewall_vip6'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['firewall_vip6']: + resp = firewall_vip6(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, - "https": {"required": False, "type": "bool", "default": "False"}, + "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "firewall_vip6": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, - "arp-reply": {"required": False, "type": "str", + "arp_reply": {"required": False, "type": "str", "choices": ["disable", "enable"]}, "color": {"required": False, "type": "int"}, "comment": {"required": False, "type": "str"}, "extip": {"required": False, "type": "str"}, "extport": {"required": False, "type": "str"}, - "http-cookie-age": {"required": False, "type": "int"}, - "http-cookie-domain": {"required": False, "type": "str"}, - "http-cookie-domain-from-host": {"required": False, "type": "str", + "http_cookie_age": {"required": False, "type": "int"}, + "http_cookie_domain": {"required": False, "type": "str"}, + "http_cookie_domain_from_host": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "http-cookie-generation": {"required": False, "type": "int"}, - "http-cookie-path": {"required": False, "type": "str"}, - "http-cookie-share": {"required": False, "type": "str", + "http_cookie_generation": {"required": False, "type": "int"}, + "http_cookie_path": {"required": False, "type": "str"}, + "http_cookie_share": {"required": False, "type": "str", "choices": ["disable", "same-ip"]}, - "http-ip-header": {"required": False, "type": "str", + "http_ip_header": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "http-ip-header-name": {"required": False, "type": "str"}, - "http-multiplex": {"required": False, "type": "str", + "http_ip_header_name": {"required": False, "type": "str"}, + "http_multiplex": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "https-cookie-secure": {"required": False, "type": "str", + "https_cookie_secure": {"required": False, "type": "str", "choices": ["disable", "enable"]}, "id": {"required": False, "type": "int"}, - "ldb-method": {"required": False, "type": "str", + "ldb_method": {"required": False, "type": "str", "choices": ["static", "round-robin", "weighted", "least-session", "least-rtt", "first-alive", "http-host"]}, "mappedip": {"required": False, "type": "str"}, "mappedport": {"required": False, "type": "str"}, - "max-embryonic-connections": {"required": False, "type": "int"}, + "max_embryonic_connections": {"required": False, "type": "int"}, "monitor": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, "name": {"required": True, "type": "str"}, - "outlook-web-access": {"required": False, "type": "str", + "outlook_web_access": {"required": False, "type": "str", "choices": ["disable", "enable"]}, "persistence": {"required": False, "type": "str", "choices": ["none", "http-cookie", "ssl-session-id"]}, @@ -822,115 +938,113 @@ def main(): "choices": ["tcp", "udp", "sctp"]}, "realservers": {"required": False, "type": "list", "options": { - "client-ip": {"required": False, "type": "str"}, + "client_ip": {"required": False, "type": "str"}, "healthcheck": {"required": False, "type": "str", "choices": ["disable", "enable", "vip"]}, - "holddown-interval": {"required": False, "type": "int"}, - "http-host": {"required": False, "type": "str"}, + "holddown_interval": {"required": False, "type": "int"}, + "http_host": {"required": False, "type": "str"}, "id": {"required": True, "type": "int"}, "ip": {"required": False, "type": "str"}, - "max-connections": {"required": False, "type": "int"}, + "max_connections": {"required": False, "type": "int"}, "monitor": {"required": False, "type": "str"}, "port": {"required": False, "type": "int"}, "status": {"required": False, "type": "str", "choices": ["active", "standby", "disable"]}, "weight": {"required": False, "type": "int"} }}, - "server-type": {"required": False, "type": "str", + "server_type": {"required": False, "type": "str", "choices": ["http", "https", "imaps", "pop3s", "smtps", "ssl", "tcp", "udp", "ip"]}, - "src-filter": {"required": False, "type": "list", + "src_filter": {"required": False, "type": "list", "options": { "range": {"required": True, "type": "str"} }}, - "ssl-algorithm": {"required": False, "type": "str", + "ssl_algorithm": {"required": False, "type": "str", "choices": ["high", "medium", "low", "custom"]}, - "ssl-certificate": {"required": False, "type": "str"}, - "ssl-cipher-suites": {"required": False, "type": "list", + "ssl_certificate": {"required": False, "type": "str"}, + "ssl_cipher_suites": {"required": False, "type": "list", "options": { "cipher": {"required": False, "type": "str", - "choices": ["TLS-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-RSA-WITH-DES-CBC-SHA", + "choices": ["TLS-RSA-WITH-3DES-EDE-CBC-SHA", "TLS-DHE-RSA-WITH-DES-CBC-SHA", "TLS-DHE-DSS-WITH-DES-CBC-SHA"]}, "priority": {"required": True, "type": "int"}, "versions": {"required": False, "type": "str", "choices": ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]} }}, - "ssl-client-fallback": {"required": False, "type": "str", + "ssl_client_fallback": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "ssl-client-renegotiation": {"required": False, "type": "str", + "ssl_client_renegotiation": {"required": False, "type": "str", "choices": ["allow", "deny", "secure"]}, - "ssl-client-session-state-max": {"required": False, "type": "int"}, - "ssl-client-session-state-timeout": {"required": False, "type": "int"}, - "ssl-client-session-state-type": {"required": False, "type": "str", + "ssl_client_session_state_max": {"required": False, "type": "int"}, + "ssl_client_session_state_timeout": {"required": False, "type": "int"}, + "ssl_client_session_state_type": {"required": False, "type": "str", "choices": ["disable", "time", "count", "both"]}, - "ssl-dh-bits": {"required": False, "type": "str", + "ssl_dh_bits": {"required": False, "type": "str", "choices": ["768", "1024", "1536", "2048", "3072", "4096"]}, - "ssl-hpkp": {"required": False, "type": "str", + "ssl_hpkp": {"required": False, "type": "str", "choices": ["disable", "enable", "report-only"]}, - "ssl-hpkp-age": {"required": False, "type": "int"}, - "ssl-hpkp-backup": {"required": False, "type": "str"}, - "ssl-hpkp-include-subdomains": {"required": False, "type": "str", + "ssl_hpkp_age": {"required": False, "type": "int"}, + "ssl_hpkp_backup": {"required": False, "type": "str"}, + "ssl_hpkp_include_subdomains": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "ssl-hpkp-primary": {"required": False, "type": "str"}, - "ssl-hpkp-report-uri": {"required": False, "type": "str"}, - "ssl-hsts": {"required": False, "type": "str", + "ssl_hpkp_primary": {"required": False, "type": "str"}, + "ssl_hpkp_report_uri": {"required": False, "type": "str"}, + "ssl_hsts": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "ssl-hsts-age": {"required": False, "type": "int"}, - "ssl-hsts-include-subdomains": {"required": False, "type": "str", + "ssl_hsts_age": {"required": False, "type": "int"}, + "ssl_hsts_include_subdomains": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "ssl-http-location-conversion": {"required": False, "type": "str", + "ssl_http_location_conversion": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ssl-http-match-host": {"required": False, "type": "str", + "ssl_http_match_host": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ssl-max-version": {"required": False, "type": "str", + "ssl_max_version": {"required": False, "type": "str", "choices": ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]}, - "ssl-min-version": {"required": False, "type": "str", + "ssl_min_version": {"required": False, "type": "str", "choices": ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]}, - "ssl-mode": {"required": False, "type": "str", + "ssl_mode": {"required": False, "type": "str", "choices": ["half", "full"]}, - "ssl-pfs": {"required": False, "type": "str", + "ssl_pfs": {"required": False, "type": "str", "choices": ["require", "deny", "allow"]}, - "ssl-send-empty-frags": {"required": False, "type": "str", + "ssl_send_empty_frags": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ssl-server-algorithm": {"required": False, "type": "str", + "ssl_server_algorithm": {"required": False, "type": "str", "choices": ["high", "medium", "low", "custom", "client"]}, - "ssl-server-cipher-suites": {"required": False, "type": "list", + "ssl_server_cipher_suites": {"required": False, "type": "list", "options": { "cipher": {"required": False, "type": "str", - "choices": ["TLS-RSA-WITH-3DES-EDE-CBC-SHA", - "TLS-DHE-RSA-WITH-DES-CBC-SHA", + "choices": ["TLS-RSA-WITH-3DES-EDE-CBC-SHA", "TLS-DHE-RSA-WITH-DES-CBC-SHA", "TLS-DHE-DSS-WITH-DES-CBC-SHA"]}, "priority": {"required": True, "type": "int"}, "versions": {"required": False, "type": "str", "choices": ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2"]} }}, - "ssl-server-max-version": {"required": False, "type": "str", + "ssl_server_max_version": {"required": False, "type": "str", "choices": ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2", "client"]}, - "ssl-server-min-version": {"required": False, "type": "str", + "ssl_server_min_version": {"required": False, "type": "str", "choices": ["ssl-3.0", "tls-1.0", "tls-1.1", "tls-1.2", "client"]}, - "ssl-server-session-state-max": {"required": False, "type": "int"}, - "ssl-server-session-state-timeout": {"required": False, "type": "int"}, - "ssl-server-session-state-type": {"required": False, "type": "str", + "ssl_server_session_state_max": {"required": False, "type": "int"}, + "ssl_server_session_state_timeout": {"required": False, "type": "int"}, + "ssl_server_session_state_type": {"required": False, "type": "str", "choices": ["disable", "time", "count", "both"]}, "type": {"required": False, "type": "str", "choices": ["static-nat", "server-load-balance"]}, "uuid": {"required": False, "type": "str"}, - "weblogic-server": {"required": False, "type": "str", + "weblogic_server": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "websphere-server": {"required": False, "type": "str", + "websphere_server": {"required": False, "type": "str", "choices": ["disable", "enable"]} } @@ -939,15 +1053,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_firewall(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_firewall(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_firewall(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_firewall_vip64.py b/lib/ansible/modules/network/fortios/fortios_firewall_vip64.py index 5d093783e78..7c45cc13903 100644 --- a/lib/ansible/modules/network/fortios/fortios_firewall_vip64.py +++ b/lib/ansible/modules/network/fortios/fortios_firewall_vip64.py @@ -1,6 +1,6 @@ #!/usr/bin/python from __future__ import (absolute_import, division, print_function) -# Copyright 2018 Fortinet, Inc. +# Copyright 2019 Fortinet, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_firewall_vip64 short_description: Configure IPv6 to IPv4 virtual IPs in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure firewall feature and vip64 category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify firewall feature and vip64 category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,64 +41,84 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool - default: false + default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 firewall_vip64: description: - Configure IPv6 to IPv4 virtual IPs. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent - arp-reply: + arp_reply: description: - Enable ARP reply. + type: str choices: - disable - enable color: description: - Color of icon on the GUI. + type: int comment: description: - Comment. + type: str extip: description: - Start-external-IP [-end-external-IP]. + type: str extport: description: - External service port. + type: str id: description: - Custom defined id. - ldb-method: + type: int + ldb_method: description: - Load balance method. + type: str choices: - static - round-robin @@ -112,69 +129,86 @@ options: mappedip: description: - Start-mapped-IP [-end-mapped-IP]. + type: str mappedport: description: - Mapped service port. + type: str monitor: description: - Health monitors. + type: list suboptions: name: description: - Health monitor name. Source firewall.ldb-monitor.name. required: true + type: str name: description: - VIP64 name. required: true + type: str portforward: description: - Enable port forwarding. + type: str choices: - disable - enable protocol: description: - Mapped port protocol. + type: str choices: - tcp - udp realservers: description: - Real servers. + type: list suboptions: - client-ip: + client_ip: description: - Restrict server to a client IP in this range. + type: str healthcheck: description: - Per server health check. + type: str choices: - disable - enable - vip - holddown-interval: + holddown_interval: description: - Hold down interval. + type: int id: description: - Real server ID. required: true + type: int ip: description: - Mapped server IP. - max-connections: + type: str + max_connections: description: - Maximum number of connections allowed to server. + type: int monitor: description: - Health monitors. Source firewall.ldb-monitor.name. + type: str port: description: - Mapped server port. + type: int status: description: - Server administrative status. + type: str choices: - active - standby @@ -182,31 +216,37 @@ options: weight: description: - weight - server-type: + type: int + server_type: description: - Server type. + type: str choices: - http - tcp - udp - ip - src-filter: + src_filter: description: - "Source IP6 filter (x:x:x:x:x:x:x:x/x)." + type: list suboptions: range: description: - Src-filter range. required: true + type: str type: description: - "VIP type: static NAT or server load balance." + type: str choices: - static-nat - server-load-balance uuid: description: - Universally Unique Identifier (UUID; automatically assigned but can be manually reset). + type: str ''' EXAMPLES = ''' @@ -216,6 +256,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPv6 to IPv4 virtual IPs. fortios_firewall_vip64: @@ -223,15 +264,16 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" + state: "present" firewall_vip64: - state: "present" - arp-reply: "disable" + arp_reply: "disable" color: "4" comment: "Comment." extip: "" extport: "" id: "8" - ldb-method: "static" + ldb_method: "static" mappedip: "" mappedport: "" monitor: @@ -242,18 +284,18 @@ EXAMPLES = ''' protocol: "tcp" realservers: - - client-ip: "" + client_ip: "" healthcheck: "disable" - holddown-interval: "20" + holddown_interval: "20" id: "21" ip: "" - max-connections: "23" + max_connections: "23" monitor: " (source firewall.ldb-monitor.name)" port: "25" status: "active" weight: "27" - server-type: "http" - src-filter: + server_type: "http" + src_filter: - range: "" type: "static-nat" @@ -280,7 +322,7 @@ mkey: description: Master key (id) used in the last call to FortiGate returned: success type: str - sample: "key1" + sample: "id" name: description: Name of the table used to fulfill the request returned: always @@ -320,14 +362,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -335,16 +379,16 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_firewall_vip64_data(json): - option_list = ['arp-reply', 'color', 'comment', + option_list = ['arp_reply', 'color', 'comment', 'extip', 'extport', 'id', - 'ldb-method', 'mappedip', 'mappedport', + 'ldb_method', 'mappedip', 'mappedport', 'monitor', 'name', 'portforward', - 'protocol', 'realservers', 'server-type', - 'src-filter', 'type', 'uuid'] + 'protocol', 'realservers', 'server_type', + 'src_filter', 'type', 'uuid'] dictionary = {} for attribute in option_list: @@ -354,56 +398,74 @@ def filter_firewall_vip64_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def firewall_vip64(data, fos): vdom = data['vdom'] + state = data['state'] firewall_vip64_data = data['firewall_vip64'] - filtered_data = filter_firewall_vip64_data(firewall_vip64_data) - if firewall_vip64_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_firewall_vip64_data(firewall_vip64_data)) + + if state == "present": return fos.set('firewall', 'vip64', data=filtered_data, vdom=vdom) - elif firewall_vip64_data['state'] == "absent": + elif state == "absent": return fos.delete('firewall', 'vip64', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_firewall(data, fos): - login(data) - methodlist = ['firewall_vip64'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['firewall_vip64']: + resp = firewall_vip64(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, - "https": {"required": False, "type": "bool", "default": "False"}, + "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "firewall_vip64": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, - "arp-reply": {"required": False, "type": "str", + "arp_reply": {"required": False, "type": "str", "choices": ["disable", "enable"]}, "color": {"required": False, "type": "int"}, "comment": {"required": False, "type": "str"}, "extip": {"required": False, "type": "str"}, "extport": {"required": False, "type": "str"}, "id": {"required": False, "type": "int"}, - "ldb-method": {"required": False, "type": "str", + "ldb_method": {"required": False, "type": "str", "choices": ["static", "round-robin", "weighted", "least-session", "least-rtt", "first-alive"]}, "mappedip": {"required": False, "type": "str"}, @@ -419,23 +481,23 @@ def main(): "choices": ["tcp", "udp"]}, "realservers": {"required": False, "type": "list", "options": { - "client-ip": {"required": False, "type": "str"}, + "client_ip": {"required": False, "type": "str"}, "healthcheck": {"required": False, "type": "str", "choices": ["disable", "enable", "vip"]}, - "holddown-interval": {"required": False, "type": "int"}, + "holddown_interval": {"required": False, "type": "int"}, "id": {"required": True, "type": "int"}, "ip": {"required": False, "type": "str"}, - "max-connections": {"required": False, "type": "int"}, + "max_connections": {"required": False, "type": "int"}, "monitor": {"required": False, "type": "str"}, "port": {"required": False, "type": "int"}, "status": {"required": False, "type": "str", "choices": ["active", "standby", "disable"]}, "weight": {"required": False, "type": "int"} }}, - "server-type": {"required": False, "type": "str", + "server_type": {"required": False, "type": "str", "choices": ["http", "tcp", "udp", "ip"]}, - "src-filter": {"required": False, "type": "list", + "src_filter": {"required": False, "type": "list", "options": { "range": {"required": True, "type": "str"} }}, @@ -449,15 +511,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_firewall(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_firewall(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_firewall(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp.py b/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp.py index f85889ca4de..16b3017c647 100644 --- a/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp.py +++ b/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp.py @@ -1,6 +1,6 @@ #!/usr/bin/python from __future__ import (absolute_import, division, print_function) -# Copyright 2018 Fortinet, Inc. +# Copyright 2019 Fortinet, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_firewall_vipgrp short_description: Configure IPv4 virtual IP groups in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure firewall feature and vipgrp category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify firewall feature and vipgrp category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,64 +41,84 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool - default: false + default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 firewall_vipgrp: description: - Configure IPv4 virtual IP groups. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent color: description: - - Integer value to determine the color of the icon in the GUI (range 1 to 32, default = 0, which sets the value to 1). + - Integer value to determine the color of the icon in the GUI (range 1 to 32). + type: int comments: description: - Comment. + type: str interface: description: - interface Source system.interface.name. + type: str member: description: - Member VIP objects of the group (Separate multiple objects with a space). + type: list suboptions: name: description: - VIP name. Source firewall.vip.name. required: true + type: str name: description: - VIP group name. required: true + type: str uuid: description: - Universally Unique Identifier (UUID; automatically assigned but can be manually reset). + type: str ''' EXAMPLES = ''' @@ -111,6 +128,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPv4 virtual IP groups. fortios_firewall_vipgrp: @@ -118,8 +136,9 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" + state: "present" firewall_vipgrp: - state: "present" color: "3" comments: "" interface: " (source system.interface.name)" @@ -150,7 +169,7 @@ mkey: description: Master key (id) used in the last call to FortiGate returned: success type: str - sample: "key1" + sample: "id" name: description: Name of the table used to fulfill the request returned: always @@ -190,14 +209,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -fos = None - -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -205,7 +226,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_firewall_vipgrp_data(json): @@ -220,48 +241,66 @@ def filter_firewall_vipgrp_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def firewall_vipgrp(data, fos): vdom = data['vdom'] + state = data['state'] firewall_vipgrp_data = data['firewall_vipgrp'] - filtered_data = filter_firewall_vipgrp_data(firewall_vipgrp_data) - if firewall_vipgrp_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_firewall_vipgrp_data(firewall_vipgrp_data)) + + if state == "present": return fos.set('firewall', 'vipgrp', data=filtered_data, vdom=vdom) - elif firewall_vipgrp_data['state'] == "absent": + elif state == "absent": return fos.delete('firewall', 'vipgrp', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_firewall(data, fos): - login(data) - methodlist = ['firewall_vipgrp'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['firewall_vipgrp']: + resp = firewall_vipgrp(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, - "https": {"required": False, "type": "bool", "default": "False"}, + "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "firewall_vipgrp": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "color": {"required": False, "type": "int"}, "comments": {"required": False, "type": "str"}, "interface": {"required": False, "type": "str"}, @@ -278,15 +317,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_firewall(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_firewall(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_firewall(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp46.py b/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp46.py index fb42f196b9d..79574e6db30 100644 --- a/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp46.py +++ b/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp46.py @@ -1,6 +1,6 @@ #!/usr/bin/python from __future__ import (absolute_import, division, print_function) -# Copyright 2018 Fortinet, Inc. +# Copyright 2019 Fortinet, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_firewall_vipgrp46 short_description: Configure IPv4 to IPv6 virtual IP groups in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure firewall feature and vipgrp46 category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify firewall feature and vipgrp46 category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,61 +41,80 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool - default: false + default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 firewall_vipgrp46: description: - Configure IPv4 to IPv6 virtual IP groups. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent color: description: - - Integer value to determine the color of the icon in the GUI (range 1 to 32, default = 0, which sets the value to 1). + - Integer value to determine the color of the icon in the GUI (range 1 to 32). + type: int comments: description: - Comment. + type: str member: description: - Member VIP objects of the group (Separate multiple objects with a space). + type: list suboptions: name: description: - VIP46 name. Source firewall.vip46.name. required: true + type: str name: description: - VIP46 group name. required: true + type: str uuid: description: - Universally Unique Identifier (UUID; automatically assigned but can be manually reset). + type: str ''' EXAMPLES = ''' @@ -108,6 +124,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPv4 to IPv6 virtual IP groups. fortios_firewall_vipgrp46: @@ -115,8 +132,9 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" + state: "present" firewall_vipgrp46: - state: "present" color: "3" comments: "" member: @@ -146,7 +164,7 @@ mkey: description: Master key (id) used in the last call to FortiGate returned: success type: str - sample: "key1" + sample: "id" name: description: Name of the table used to fulfill the request returned: always @@ -186,14 +204,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -fos = None - -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -201,7 +221,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_firewall_vipgrp46_data(json): @@ -216,48 +236,66 @@ def filter_firewall_vipgrp46_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def firewall_vipgrp46(data, fos): vdom = data['vdom'] + state = data['state'] firewall_vipgrp46_data = data['firewall_vipgrp46'] - filtered_data = filter_firewall_vipgrp46_data(firewall_vipgrp46_data) - if firewall_vipgrp46_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_firewall_vipgrp46_data(firewall_vipgrp46_data)) + + if state == "present": return fos.set('firewall', 'vipgrp46', data=filtered_data, vdom=vdom) - elif firewall_vipgrp46_data['state'] == "absent": + elif state == "absent": return fos.delete('firewall', 'vipgrp46', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_firewall(data, fos): - login(data) - methodlist = ['firewall_vipgrp46'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['firewall_vipgrp46']: + resp = firewall_vipgrp46(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, - "https": {"required": False, "type": "bool", "default": "False"}, + "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "firewall_vipgrp46": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "color": {"required": False, "type": "int"}, "comments": {"required": False, "type": "str"}, "member": {"required": False, "type": "list", @@ -273,15 +311,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_firewall(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_firewall(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_firewall(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp6.py b/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp6.py index b3556c00ac2..87ac72b79c8 100644 --- a/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp6.py +++ b/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp6.py @@ -1,6 +1,6 @@ #!/usr/bin/python from __future__ import (absolute_import, division, print_function) -# Copyright 2018 Fortinet, Inc. +# Copyright 2019 Fortinet, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_firewall_vipgrp6 short_description: Configure IPv6 virtual IP groups in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure firewall feature and vipgrp6 category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify firewall feature and vipgrp6 category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,61 +41,80 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool - default: false + default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 firewall_vipgrp6: description: - Configure IPv6 virtual IP groups. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent color: description: - - Integer value to determine the color of the icon in the GUI (range 1 to 32, default = 0, which sets the value to 1). + - Integer value to determine the color of the icon in the GUI (range 1 to 32). + type: int comments: description: - Comment. + type: str member: description: - Member VIP objects of the group (Separate multiple objects with a space). + type: list suboptions: name: description: - IPv6 VIP name. Source firewall.vip6.name. required: true + type: str name: description: - IPv6 VIP group name. required: true + type: str uuid: description: - Universally Unique Identifier (UUID; automatically assigned but can be manually reset). + type: str ''' EXAMPLES = ''' @@ -108,6 +124,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPv6 virtual IP groups. fortios_firewall_vipgrp6: @@ -115,8 +132,9 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" + state: "present" firewall_vipgrp6: - state: "present" color: "3" comments: "" member: @@ -146,7 +164,7 @@ mkey: description: Master key (id) used in the last call to FortiGate returned: success type: str - sample: "key1" + sample: "id" name: description: Name of the table used to fulfill the request returned: always @@ -186,14 +204,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -fos = None - -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -201,7 +221,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_firewall_vipgrp6_data(json): @@ -216,48 +236,66 @@ def filter_firewall_vipgrp6_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def firewall_vipgrp6(data, fos): vdom = data['vdom'] + state = data['state'] firewall_vipgrp6_data = data['firewall_vipgrp6'] - filtered_data = filter_firewall_vipgrp6_data(firewall_vipgrp6_data) - if firewall_vipgrp6_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_firewall_vipgrp6_data(firewall_vipgrp6_data)) + + if state == "present": return fos.set('firewall', 'vipgrp6', data=filtered_data, vdom=vdom) - elif firewall_vipgrp6_data['state'] == "absent": + elif state == "absent": return fos.delete('firewall', 'vipgrp6', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_firewall(data, fos): - login(data) - methodlist = ['firewall_vipgrp6'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['firewall_vipgrp6']: + resp = firewall_vipgrp6(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, - "https": {"required": False, "type": "bool", "default": "False"}, + "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "firewall_vipgrp6": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "color": {"required": False, "type": "int"}, "comments": {"required": False, "type": "str"}, "member": {"required": False, "type": "list", @@ -273,15 +311,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_firewall(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_firewall(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_firewall(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp64.py b/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp64.py index 62c4f117d0b..e0e48058237 100644 --- a/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp64.py +++ b/lib/ansible/modules/network/fortios/fortios_firewall_vipgrp64.py @@ -1,6 +1,6 @@ #!/usr/bin/python from __future__ import (absolute_import, division, print_function) -# Copyright 2018 Fortinet, Inc. +# Copyright 2019 Fortinet, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_firewall_vipgrp64 short_description: Configure IPv6 to IPv4 virtual IP groups in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure firewall feature and vipgrp64 category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify firewall feature and vipgrp64 category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,61 +41,80 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool - default: false + default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 firewall_vipgrp64: description: - Configure IPv6 to IPv4 virtual IP groups. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent color: description: - - Integer value to determine the color of the icon in the GUI (range 1 to 32, default = 0, which sets the value to 1). + - Integer value to determine the color of the icon in the GUI (range 1 to 32). + type: int comments: description: - Comment. + type: str member: description: - Member VIP objects of the group (Separate multiple objects with a space). + type: list suboptions: name: description: - VIP64 name. Source firewall.vip64.name. required: true + type: str name: description: - VIP64 group name. required: true + type: str uuid: description: - Universally Unique Identifier (UUID; automatically assigned but can be manually reset). + type: str ''' EXAMPLES = ''' @@ -108,6 +124,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPv6 to IPv4 virtual IP groups. fortios_firewall_vipgrp64: @@ -115,8 +132,9 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" + state: "present" firewall_vipgrp64: - state: "present" color: "3" comments: "" member: @@ -146,7 +164,7 @@ mkey: description: Master key (id) used in the last call to FortiGate returned: success type: str - sample: "key1" + sample: "id" name: description: Name of the table used to fulfill the request returned: always @@ -186,14 +204,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -fos = None - -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -201,7 +221,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_firewall_vipgrp64_data(json): @@ -216,48 +236,66 @@ def filter_firewall_vipgrp64_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def firewall_vipgrp64(data, fos): vdom = data['vdom'] + state = data['state'] firewall_vipgrp64_data = data['firewall_vipgrp64'] - filtered_data = filter_firewall_vipgrp64_data(firewall_vipgrp64_data) - if firewall_vipgrp64_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_firewall_vipgrp64_data(firewall_vipgrp64_data)) + + if state == "present": return fos.set('firewall', 'vipgrp64', data=filtered_data, vdom=vdom) - elif firewall_vipgrp64_data['state'] == "absent": + elif state == "absent": return fos.delete('firewall', 'vipgrp64', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_firewall(data, fos): - login(data) - methodlist = ['firewall_vipgrp64'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['firewall_vipgrp64']: + resp = firewall_vipgrp64(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, - "https": {"required": False, "type": "bool", "default": "False"}, + "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "firewall_vipgrp64": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "color": {"required": False, "type": "int"}, "comments": {"required": False, "type": "str"}, "member": {"required": False, "type": "list", @@ -273,15 +311,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_firewall(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_firewall(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_firewall(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_firewall_wildcard_fqdn_custom.py b/lib/ansible/modules/network/fortios/fortios_firewall_wildcard_fqdn_custom.py index e09324b5167..92385e744b3 100644 --- a/lib/ansible/modules/network/fortios/fortios_firewall_wildcard_fqdn_custom.py +++ b/lib/ansible/modules/network/fortios/fortios_firewall_wildcard_fqdn_custom.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_firewall_wildcard_fqdn_custom short_description: Config global/VDOM Wildcard FQDN address in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure firewall_wildcard_fqdn feature and custom category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify firewall_wildcard_fqdn feature and custom category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,62 +41,81 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip adress. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 firewall_wildcard_fqdn_custom: description: - Config global/VDOM Wildcard FQDN address. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent color: description: - GUI icon color. + type: int comment: description: - Comment. + type: str name: description: - Address name. required: true + type: str uuid: description: - Universally Unique Identifier (UUID; automatically assigned but can be manually reset). + type: str visibility: description: - Enable/disable address visibility. + type: str choices: - enable - disable - wildcard-fqdn: + wildcard_fqdn: description: - Wildcard FQDN. + type: str ''' EXAMPLES = ''' @@ -109,6 +125,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Config global/VDOM Wildcard FQDN address. fortios_firewall_wildcard_fqdn_custom: @@ -117,14 +134,14 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" firewall_wildcard_fqdn_custom: - state: "present" color: "3" comment: "Comment." name: "default_name_5" uuid: "" visibility: "enable" - wildcard-fqdn: "" + wildcard_fqdn: "" ''' RETURN = ''' @@ -187,14 +204,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -202,12 +221,12 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_firewall_wildcard_fqdn_custom_data(json): option_list = ['color', 'comment', 'name', - 'uuid', 'visibility', 'wildcard-fqdn'] + 'uuid', 'visibility', 'wildcard_fqdn'] dictionary = {} for attribute in option_list: @@ -217,55 +236,73 @@ def filter_firewall_wildcard_fqdn_custom_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def firewall_wildcard_fqdn_custom(data, fos): vdom = data['vdom'] + state = data['state'] firewall_wildcard_fqdn_custom_data = data['firewall_wildcard_fqdn_custom'] - filtered_data = filter_firewall_wildcard_fqdn_custom_data(firewall_wildcard_fqdn_custom_data) - if firewall_wildcard_fqdn_custom_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_firewall_wildcard_fqdn_custom_data(firewall_wildcard_fqdn_custom_data)) + + if state == "present": return fos.set('firewall.wildcard-fqdn', 'custom', data=filtered_data, vdom=vdom) - elif firewall_wildcard_fqdn_custom_data['state'] == "absent": + elif state == "absent": return fos.delete('firewall.wildcard-fqdn', 'custom', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_firewall_wildcard_fqdn(data, fos): - login(data) - methodlist = ['firewall_wildcard_fqdn_custom'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['firewall_wildcard_fqdn_custom']: + resp = firewall_wildcard_fqdn_custom(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "firewall_wildcard_fqdn_custom": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "color": {"required": False, "type": "int"}, "comment": {"required": False, "type": "str"}, "name": {"required": True, "type": "str"}, "uuid": {"required": False, "type": "str"}, "visibility": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "wildcard-fqdn": {"required": False, "type": "str"} + "wildcard_fqdn": {"required": False, "type": "str"} } } @@ -273,15 +310,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_firewall_wildcard_fqdn(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_firewall_wildcard_fqdn(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_firewall_wildcard_fqdn(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_firewall_wildcard_fqdn_group.py b/lib/ansible/modules/network/fortios/fortios_firewall_wildcard_fqdn_group.py index 6565b44ca6d..ccb445378de 100644 --- a/lib/ansible/modules/network/fortios/fortios_firewall_wildcard_fqdn_group.py +++ b/lib/ansible/modules/network/fortios/fortios_firewall_wildcard_fqdn_group.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_firewall_wildcard_fqdn_group short_description: Config global Wildcard FQDN address groups in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure firewall_wildcard_fqdn feature and group category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify firewall_wildcard_fqdn feature and group category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,64 +41,84 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip adress. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 firewall_wildcard_fqdn_group: description: - Config global Wildcard FQDN address groups. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent color: description: - GUI icon color. + type: int comment: description: - Comment. + type: str member: description: - Address group members. + type: list suboptions: name: description: - Address name. Source firewall.wildcard-fqdn.custom.name. required: true + type: str name: description: - Address group name. required: true + type: str uuid: description: - Universally Unique Identifier (UUID; automatically assigned but can be manually reset). + type: str visibility: description: - Enable/disable address visibility. + type: str choices: - enable - disable @@ -114,6 +131,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Config global Wildcard FQDN address groups. fortios_firewall_wildcard_fqdn_group: @@ -122,8 +140,8 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" firewall_wildcard_fqdn_group: - state: "present" color: "3" comment: "Comment." member: @@ -194,14 +212,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -209,7 +229,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_firewall_wildcard_fqdn_group_data(json): @@ -224,48 +244,66 @@ def filter_firewall_wildcard_fqdn_group_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def firewall_wildcard_fqdn_group(data, fos): vdom = data['vdom'] + state = data['state'] firewall_wildcard_fqdn_group_data = data['firewall_wildcard_fqdn_group'] - filtered_data = filter_firewall_wildcard_fqdn_group_data(firewall_wildcard_fqdn_group_data) - if firewall_wildcard_fqdn_group_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_firewall_wildcard_fqdn_group_data(firewall_wildcard_fqdn_group_data)) + + if state == "present": return fos.set('firewall.wildcard-fqdn', 'group', data=filtered_data, vdom=vdom) - elif firewall_wildcard_fqdn_group_data['state'] == "absent": + elif state == "absent": return fos.delete('firewall.wildcard-fqdn', 'group', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_firewall_wildcard_fqdn(data, fos): - login(data) - methodlist = ['firewall_wildcard_fqdn_group'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['firewall_wildcard_fqdn_group']: + resp = firewall_wildcard_fqdn_group(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "firewall_wildcard_fqdn_group": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "color": {"required": False, "type": "int"}, "comment": {"required": False, "type": "str"}, "member": {"required": False, "type": "list", @@ -283,15 +321,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_firewall_wildcard_fqdn(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_firewall_wildcard_fqdn(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_firewall_wildcard_fqdn(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_ftp_proxy_explicit.py b/lib/ansible/modules/network/fortios/fortios_ftp_proxy_explicit.py index 3532a722d9c..fd394d9f442 100644 --- a/lib/ansible/modules/network/fortios/fortios_ftp_proxy_explicit.py +++ b/lib/ansible/modules/network/fortios/fortios_ftp_proxy_explicit.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_ftp_proxy_explicit short_description: Configure explicit FTP proxy settings in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure ftp_proxy feature and explicit category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify ftp_proxy feature and explicit category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,52 +41,67 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip adress. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 ftp_proxy_explicit: description: - Configure explicit FTP proxy settings. default: null + type: dict suboptions: - incoming-ip: + incoming_ip: description: - Accept incoming FTP requests from this IP address. An interface must have this IP address. - incoming-port: + type: str + incoming_port: description: - Accept incoming FTP requests on one or more ports. - outgoing-ip: + type: str + outgoing_ip: description: - Outgoing FTP requests will leave from this IP address. An interface must have this IP address. - sec-default-action: + type: str + sec_default_action: description: - Accept or deny explicit FTP proxy sessions when no FTP proxy firewall policy exists. + type: str choices: - accept - deny status: description: - Enable/disable the explicit FTP proxy. + type: str choices: - enable - disable @@ -102,6 +114,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure explicit FTP proxy settings. fortios_ftp_proxy_explicit: @@ -111,10 +124,10 @@ EXAMPLES = ''' vdom: "{{ vdom }}" https: "False" ftp_proxy_explicit: - incoming-ip: "" - incoming-port: "" - outgoing-ip: "" - sec-default-action: "accept" + incoming_ip: "" + incoming_port: "" + outgoing_ip: "" + sec_default_action: "accept" status: "enable" ''' @@ -178,14 +191,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -193,12 +208,12 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_ftp_proxy_explicit_data(json): - option_list = ['incoming-ip', 'incoming-port', 'outgoing-ip', - 'sec-default-action', 'status'] + option_list = ['incoming_ip', 'incoming_port', 'outgoing_ip', + 'sec_default_action', 'status'] dictionary = {} for attribute in option_list: @@ -208,43 +223,60 @@ def filter_ftp_proxy_explicit_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def ftp_proxy_explicit(data, fos): vdom = data['vdom'] ftp_proxy_explicit_data = data['ftp_proxy_explicit'] - filtered_data = filter_ftp_proxy_explicit_data(ftp_proxy_explicit_data) + filtered_data = underscore_to_hyphen(filter_ftp_proxy_explicit_data(ftp_proxy_explicit_data)) + return fos.set('ftp-proxy', 'explicit', data=filtered_data, vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_ftp_proxy(data, fos): - login(data) - methodlist = ['ftp_proxy_explicit'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['ftp_proxy_explicit']: + resp = ftp_proxy_explicit(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, "ftp_proxy_explicit": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "incoming-ip": {"required": False, "type": "str"}, - "incoming-port": {"required": False, "type": "str"}, - "outgoing-ip": {"required": False, "type": "str"}, - "sec-default-action": {"required": False, "type": "str", + "incoming_ip": {"required": False, "type": "str"}, + "incoming_port": {"required": False, "type": "str"}, + "outgoing_ip": {"required": False, "type": "str"}, + "sec_default_action": {"required": False, "type": "str", "choices": ["accept", "deny"]}, "status": {"required": False, "type": "str", "choices": ["enable", "disable"]} @@ -255,15 +287,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_ftp_proxy(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_ftp_proxy(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_ftp_proxy(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_icap_profile.py b/lib/ansible/modules/network/fortios/fortios_icap_profile.py index 3f3ed997e35..9ca910e07ee 100644 --- a/lib/ansible/modules/network/fortios/fortios_icap_profile.py +++ b/lib/ansible/modules/network/fortios/fortios_icap_profile.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_icap_profile short_description: Configure ICAP profiles in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure icap feature and profile category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify icap feature and profile category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,43 +41,57 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip adress. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 icap_profile: description: - Configure ICAP profiles. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent methods: description: - The allowed HTTP methods that will be sent to ICAP server for further processing. + type: str choices: - delete - get @@ -94,48 +105,59 @@ options: description: - ICAP profile name. required: true - replacemsg-group: + type: str + replacemsg_group: description: - Replacement message group. Source system.replacemsg-group.name. + type: str request: description: - Enable/disable whether an HTTP request is passed to an ICAP server. + type: str choices: - disable - enable - request-failure: + request_failure: description: - Action to take if the ICAP server cannot be contacted when processing an HTTP request. + type: str choices: - error - bypass - request-path: + request_path: description: - Path component of the ICAP URI that identifies the HTTP request processing service. - request-server: + type: str + request_server: description: - ICAP server to use for an HTTP request. Source icap.server.name. + type: str response: description: - Enable/disable whether an HTTP response is passed to an ICAP server. + type: str choices: - disable - enable - response-failure: + response_failure: description: - Action to take if the ICAP server cannot be contacted when processing an HTTP response. + type: str choices: - error - bypass - response-path: + response_path: description: - Path component of the ICAP URI that identifies the HTTP response processing service. - response-server: + type: str + response_server: description: - ICAP server to use for an HTTP response. Source icap.server.name. - streaming-content-bypass: + type: str + streaming_content_bypass: description: - Enable/disable bypassing of ICAP server for streaming content. + type: str choices: - disable - enable @@ -148,6 +170,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure ICAP profiles. fortios_icap_profile: @@ -156,20 +179,20 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" icap_profile: - state: "present" methods: "delete" name: "default_name_4" - replacemsg-group: " (source system.replacemsg-group.name)" + replacemsg_group: " (source system.replacemsg-group.name)" request: "disable" - request-failure: "error" - request-path: "" - request-server: " (source icap.server.name)" + request_failure: "error" + request_path: "" + request_server: " (source icap.server.name)" response: "disable" - response-failure: "error" - response-path: "" - response-server: " (source icap.server.name)" - streaming-content-bypass: "disable" + response_failure: "error" + response_path: "" + response_server: " (source icap.server.name)" + streaming_content_bypass: "disable" ''' RETURN = ''' @@ -232,14 +255,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -247,14 +272,14 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_icap_profile_data(json): - option_list = ['methods', 'name', 'replacemsg-group', - 'request', 'request-failure', 'request-path', - 'request-server', 'response', 'response-failure', - 'response-path', 'response-server', 'streaming-content-bypass'] + option_list = ['methods', 'name', 'replacemsg_group', + 'request', 'request_failure', 'request_path', + 'request_server', 'response', 'response_failure', + 'response_path', 'response_server', 'streaming_content_bypass'] dictionary = {} for attribute in option_list: @@ -264,67 +289,85 @@ def filter_icap_profile_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def icap_profile(data, fos): vdom = data['vdom'] + state = data['state'] icap_profile_data = data['icap_profile'] - filtered_data = filter_icap_profile_data(icap_profile_data) - if icap_profile_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_icap_profile_data(icap_profile_data)) + + if state == "present": return fos.set('icap', 'profile', data=filtered_data, vdom=vdom) - elif icap_profile_data['state'] == "absent": + elif state == "absent": return fos.delete('icap', 'profile', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_icap(data, fos): - login(data) - methodlist = ['icap_profile'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['icap_profile']: + resp = icap_profile(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "icap_profile": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "methods": {"required": False, "type": "str", "choices": ["delete", "get", "head", "options", "post", "put", "trace", "other"]}, "name": {"required": True, "type": "str"}, - "replacemsg-group": {"required": False, "type": "str"}, + "replacemsg_group": {"required": False, "type": "str"}, "request": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "request-failure": {"required": False, "type": "str", + "request_failure": {"required": False, "type": "str", "choices": ["error", "bypass"]}, - "request-path": {"required": False, "type": "str"}, - "request-server": {"required": False, "type": "str"}, + "request_path": {"required": False, "type": "str"}, + "request_server": {"required": False, "type": "str"}, "response": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "response-failure": {"required": False, "type": "str", + "response_failure": {"required": False, "type": "str", "choices": ["error", "bypass"]}, - "response-path": {"required": False, "type": "str"}, - "response-server": {"required": False, "type": "str"}, - "streaming-content-bypass": {"required": False, "type": "str", + "response_path": {"required": False, "type": "str"}, + "response_server": {"required": False, "type": "str"}, + "streaming_content_bypass": {"required": False, "type": "str", "choices": ["disable", "enable"]} } @@ -333,15 +376,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_icap(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_icap(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_icap(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_icap_server.py b/lib/ansible/modules/network/fortios/fortios_icap_server.py index 65bc37436bf..2e14636edbf 100644 --- a/lib/ansible/modules/network/fortios/fortios_icap_server.py +++ b/lib/ansible/modules/network/fortios/fortios_icap_server.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_icap_server short_description: Configure ICAP servers in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure icap feature and server category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify icap feature and server category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,62 +41,81 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip adress. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 icap_server: description: - Configure ICAP servers. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent - ip-address: + ip_address: description: - IPv4 address of the ICAP server. - ip-version: + type: str + ip_version: description: - IP version. + type: str choices: - 4 - 6 - ip6-address: + ip6_address: description: - IPv6 address of the ICAP server. - max-connections: + type: str + max_connections: description: - Maximum number of concurrent connections to ICAP server. + type: int name: description: - Server name. required: true + type: str port: description: - ICAP server port. + type: int ''' EXAMPLES = ''' @@ -109,6 +125,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure ICAP servers. fortios_icap_server: @@ -117,12 +134,12 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" icap_server: - state: "present" - ip-address: "" - ip-version: "4" - ip6-address: "" - max-connections: "6" + ip_address: "" + ip_version: "4" + ip6_address: "" + max_connections: "6" name: "default_name_7" port: "8" ''' @@ -187,14 +204,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -202,12 +221,12 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_icap_server_data(json): - option_list = ['ip-address', 'ip-version', 'ip6-address', - 'max-connections', 'name', 'port'] + option_list = ['ip_address', 'ip_version', 'ip6_address', + 'max_connections', 'name', 'port'] dictionary = {} for attribute in option_list: @@ -217,53 +236,71 @@ def filter_icap_server_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def icap_server(data, fos): vdom = data['vdom'] + state = data['state'] icap_server_data = data['icap_server'] - filtered_data = filter_icap_server_data(icap_server_data) - if icap_server_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_icap_server_data(icap_server_data)) + + if state == "present": return fos.set('icap', 'server', data=filtered_data, vdom=vdom) - elif icap_server_data['state'] == "absent": + elif state == "absent": return fos.delete('icap', 'server', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_icap(data, fos): - login(data) - methodlist = ['icap_server'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['icap_server']: + resp = icap_server(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "icap_server": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, - "ip-address": {"required": False, "type": "str"}, - "ip-version": {"required": False, "type": "str", + "ip_address": {"required": False, "type": "str"}, + "ip_version": {"required": False, "type": "str", "choices": ["4", "6"]}, - "ip6-address": {"required": False, "type": "str"}, - "max-connections": {"required": False, "type": "int"}, + "ip6_address": {"required": False, "type": "str"}, + "max_connections": {"required": False, "type": "int"}, "name": {"required": True, "type": "str"}, "port": {"required": False, "type": "int"} @@ -273,15 +310,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_icap(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_icap(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_icap(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_ips_custom.py b/lib/ansible/modules/network/fortios/fortios_ips_custom.py index 920b238850a..d1672e12ea0 100644 --- a/lib/ansible/modules/network/fortios/fortios_ips_custom.py +++ b/lib/ansible/modules/network/fortios/fortios_ips_custom.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_ips_custom short_description: Configure IPS custom signature in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure ips feature and custom category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify ips feature and custom category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,88 +41,114 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip adress. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 ips_custom: description: - Configure IPS custom signature. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent action: description: - Default action (pass or block) for this signature. + type: str choices: - pass - block application: description: - Applications to be protected. Blank for all applications. + type: str comment: description: - Comment. + type: str location: description: - Protect client or server traffic. + type: str log: description: - Enable/disable logging. + type: str choices: - disable - enable - log-packet: + log_packet: description: - Enable/disable packet logging. + type: str choices: - disable - enable os: description: - Operating system(s) that the signature protects. Blank for all operating systems. + type: str protocol: description: - Protocol(s) that the signature scans. Blank for all protocols. - rule-id: + type: str + rule_id: description: - Signature ID. + type: int severity: description: - Relative severity of the signature, from info to critical. Log messages generated by the signature include the severity. - sig-name: + type: str + sig_name: description: - Signature name. + type: str signature: description: - Custom signature enclosed in single quotes. + type: str status: description: - Enable/disable this signature. + type: str choices: - disable - enable @@ -133,6 +156,7 @@ options: description: - Signature tag. required: true + type: str ''' EXAMPLES = ''' @@ -142,6 +166,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPS custom signature. fortios_ips_custom: @@ -150,19 +175,19 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" ips_custom: - state: "present" action: "pass" application: "" comment: "Comment." location: "" log: "disable" - log-packet: "disable" + log_packet: "disable" os: "" protocol: "" - rule-id: "11" + rule_id: "11" severity: "" - sig-name: "" + sig_name: "" signature: "" status: "disable" tag: "" @@ -228,14 +253,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -243,14 +270,14 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_ips_custom_data(json): option_list = ['action', 'application', 'comment', - 'location', 'log', 'log-packet', - 'os', 'protocol', 'rule-id', - 'severity', 'sig-name', 'signature', + 'location', 'log', 'log_packet', + 'os', 'protocol', 'rule_id', + 'severity', 'sig_name', 'signature', 'status', 'tag'] dictionary = {} @@ -261,48 +288,66 @@ def filter_ips_custom_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def ips_custom(data, fos): vdom = data['vdom'] + state = data['state'] ips_custom_data = data['ips_custom'] - filtered_data = filter_ips_custom_data(ips_custom_data) - if ips_custom_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_ips_custom_data(ips_custom_data)) + + if state == "present": return fos.set('ips', 'custom', data=filtered_data, vdom=vdom) - elif ips_custom_data['state'] == "absent": + elif state == "absent": return fos.delete('ips', 'custom', mkey=filtered_data['tag'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_ips(data, fos): - login(data) - methodlist = ['ips_custom'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['ips_custom']: + resp = ips_custom(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "ips_custom": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "action": {"required": False, "type": "str", "choices": ["pass", "block"]}, "application": {"required": False, "type": "str"}, @@ -310,13 +355,13 @@ def main(): "location": {"required": False, "type": "str"}, "log": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "log-packet": {"required": False, "type": "str", + "log_packet": {"required": False, "type": "str", "choices": ["disable", "enable"]}, "os": {"required": False, "type": "str"}, "protocol": {"required": False, "type": "str"}, - "rule-id": {"required": False, "type": "int"}, + "rule_id": {"required": False, "type": "int"}, "severity": {"required": False, "type": "str"}, - "sig-name": {"required": False, "type": "str"}, + "sig_name": {"required": False, "type": "str"}, "signature": {"required": False, "type": "str"}, "status": {"required": False, "type": "str", "choices": ["disable", "enable"]}, @@ -328,15 +373,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_ips(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_ips(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_ips(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_ips_decoder.py b/lib/ansible/modules/network/fortios/fortios_ips_decoder.py index 5ec15b5cdf9..9995021feb1 100644 --- a/lib/ansible/modules/network/fortios/fortios_ips_decoder.py +++ b/lib/ansible/modules/network/fortios/fortios_ips_decoder.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_ips_decoder short_description: Configure IPS decoder in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure ips feature and decoder category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify ips feature and decoder category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,55 +41,72 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip adress. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 ips_decoder: description: - Configure IPS decoder. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent name: description: - Decoder name. required: true + type: str parameter: description: - IPS group parameters. + type: list suboptions: name: description: - Parameter name. required: true + type: str value: description: - Parameter value. + type: str ''' EXAMPLES = ''' @@ -102,6 +116,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPS decoder. fortios_ips_decoder: @@ -110,8 +125,8 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" ips_decoder: - state: "present" name: "default_name_3" parameter: - @@ -179,14 +194,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -194,7 +211,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_ips_decoder_data(json): @@ -208,48 +225,66 @@ def filter_ips_decoder_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def ips_decoder(data, fos): vdom = data['vdom'] + state = data['state'] ips_decoder_data = data['ips_decoder'] - filtered_data = filter_ips_decoder_data(ips_decoder_data) - if ips_decoder_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_ips_decoder_data(ips_decoder_data)) + + if state == "present": return fos.set('ips', 'decoder', data=filtered_data, vdom=vdom) - elif ips_decoder_data['state'] == "absent": + elif state == "absent": return fos.delete('ips', 'decoder', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_ips(data, fos): - login(data) - methodlist = ['ips_decoder'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['ips_decoder']: + resp = ips_decoder(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "ips_decoder": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "name": {"required": True, "type": "str"}, "parameter": {"required": False, "type": "list", "options": { @@ -263,15 +298,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_ips(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_ips(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_ips(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_ips_global.py b/lib/ansible/modules/network/fortios/fortios_ips_global.py index 2b20d593cab..43134ff3322 100644 --- a/lib/ansible/modules/network/fortios/fortios_ips_global.py +++ b/lib/ansible/modules/network/fortios/fortios_ips_global.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_ips_global short_description: Configure IPS global parameter in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure ips feature and global category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify ips feature and global category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,37 +41,48 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip adress. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 ips_global: description: - Configure IPS global parameter. default: null + type: dict suboptions: - anomaly-mode: + anomaly_mode: description: - Global blocking mode for rate-based anomalies. + type: str choices: - periodical - continuous @@ -82,59 +90,71 @@ options: description: - Regular or extended IPS database. Regular protects against the latest common and in-the-wild attacks. Extended includes protection from legacy attacks. + type: str choices: - regular - extended - deep-app-insp-db-limit: + deep_app_insp_db_limit: description: - Limit on number of entries in deep application inspection database (1 - 2147483647, 0 = use recommended setting) - deep-app-insp-timeout: + type: int + deep_app_insp_timeout: description: - Timeout for Deep application inspection (1 - 2147483647 sec., 0 = use recommended setting). - engine-count: + type: int + engine_count: description: - Number of IPS engines running. If set to the default value of 0, FortiOS sets the number to optimize performance depending on the number of CPU cores. - exclude-signatures: + type: int + exclude_signatures: description: - Excluded signatures. + type: str choices: - none - industrial - fail-open: + fail_open: description: - Enable to allow traffic if the IPS process crashes. Default is disable and IPS traffic is blocked when the IPS process crashes. + type: str choices: - enable - disable - intelligent-mode: + intelligent_mode: description: - Enable/disable IPS adaptive scanning (intelligent mode). Intelligent mode optimizes the scanning method for the type of traffic. + type: str choices: - enable - disable - session-limit-mode: + session_limit_mode: description: - Method of counting concurrent sessions used by session limit anomalies. Choose between greater accuracy (accurate) or improved performance (heuristics). + type: str choices: - accurate - heuristic - skype-client-public-ipaddr: + skype_client_public_ipaddr: description: - Public IP addresses of your network that receive Skype sessions. Helps identify Skype sessions. Separate IP addresses with commas. - socket-size: + type: str + socket_size: description: - IPS socket buffer size (0 - 256 MB). Default depends on available memory. Can be changed to tune performance. - sync-session-ttl: + type: int + sync_session_ttl: description: - Enable/disable use of kernel session TTL for IPS sessions. + type: str choices: - enable - disable - traffic-submit: + traffic_submit: description: - Enable/disable submitting attack data found by this FortiGate to FortiGuard. + type: str choices: - enable - disable @@ -147,6 +167,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPS global parameter. fortios_ips_global: @@ -156,19 +177,19 @@ EXAMPLES = ''' vdom: "{{ vdom }}" https: "False" ips_global: - anomaly-mode: "periodical" + anomaly_mode: "periodical" database: "regular" - deep-app-insp-db-limit: "5" - deep-app-insp-timeout: "6" - engine-count: "7" - exclude-signatures: "none" - fail-open: "enable" - intelligent-mode: "enable" - session-limit-mode: "accurate" - skype-client-public-ipaddr: "" - socket-size: "13" - sync-session-ttl: "enable" - traffic-submit: "enable" + deep_app_insp_db_limit: "5" + deep_app_insp_timeout: "6" + engine_count: "7" + exclude_signatures: "none" + fail_open: "enable" + intelligent_mode: "enable" + session_limit_mode: "accurate" + skype_client_public_ipaddr: "" + socket_size: "13" + sync_session_ttl: "enable" + traffic_submit: "enable" ''' RETURN = ''' @@ -231,14 +252,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -246,15 +269,15 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_ips_global_data(json): - option_list = ['anomaly-mode', 'database', 'deep-app-insp-db-limit', - 'deep-app-insp-timeout', 'engine-count', 'exclude-signatures', - 'fail-open', 'intelligent-mode', 'session-limit-mode', - 'skype-client-public-ipaddr', 'socket-size', 'sync-session-ttl', - 'traffic-submit'] + option_list = ['anomaly_mode', 'database', 'deep_app_insp_db_limit', + 'deep_app_insp_timeout', 'engine_count', 'exclude_signatures', + 'fail_open', 'intelligent_mode', 'session_limit_mode', + 'skype_client_public_ipaddr', 'socket_size', 'sync_session_ttl', + 'traffic_submit'] dictionary = {} for attribute in option_list: @@ -264,59 +287,76 @@ def filter_ips_global_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def ips_global(data, fos): vdom = data['vdom'] ips_global_data = data['ips_global'] - filtered_data = filter_ips_global_data(ips_global_data) + filtered_data = underscore_to_hyphen(filter_ips_global_data(ips_global_data)) + return fos.set('ips', 'global', data=filtered_data, vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_ips(data, fos): - login(data) - methodlist = ['ips_global'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['ips_global']: + resp = ips_global(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, "ips_global": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "anomaly-mode": {"required": False, "type": "str", + "anomaly_mode": {"required": False, "type": "str", "choices": ["periodical", "continuous"]}, "database": {"required": False, "type": "str", "choices": ["regular", "extended"]}, - "deep-app-insp-db-limit": {"required": False, "type": "int"}, - "deep-app-insp-timeout": {"required": False, "type": "int"}, - "engine-count": {"required": False, "type": "int"}, - "exclude-signatures": {"required": False, "type": "str", + "deep_app_insp_db_limit": {"required": False, "type": "int"}, + "deep_app_insp_timeout": {"required": False, "type": "int"}, + "engine_count": {"required": False, "type": "int"}, + "exclude_signatures": {"required": False, "type": "str", "choices": ["none", "industrial"]}, - "fail-open": {"required": False, "type": "str", + "fail_open": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "intelligent-mode": {"required": False, "type": "str", + "intelligent_mode": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "session-limit-mode": {"required": False, "type": "str", + "session_limit_mode": {"required": False, "type": "str", "choices": ["accurate", "heuristic"]}, - "skype-client-public-ipaddr": {"required": False, "type": "str"}, - "socket-size": {"required": False, "type": "int"}, - "sync-session-ttl": {"required": False, "type": "str", + "skype_client_public_ipaddr": {"required": False, "type": "str"}, + "socket_size": {"required": False, "type": "int"}, + "sync_session_ttl": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "traffic-submit": {"required": False, "type": "str", + "traffic_submit": {"required": False, "type": "str", "choices": ["enable", "disable"]} } @@ -325,15 +365,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_ips(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_ips(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_ips(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_ips_rule.py b/lib/ansible/modules/network/fortios/fortios_ips_rule.py index cb8b13df296..aadb3b22868 100644 --- a/lib/ansible/modules/network/fortios/fortios_ips_rule.py +++ b/lib/ansible/modules/network/fortios/fortios_ips_rule.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_ips_rule short_description: Configure IPS rules in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure ips feature and rule category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify ips feature and rule category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,106 +41,137 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip adress. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 ips_rule: description: - Configure IPS rules. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent action: description: - Action. + type: str choices: - pass - block application: description: - Vulnerable applications. + type: str date: description: - Date. + type: int group: description: - Group. + type: str location: description: - Vulnerable location. + type: str log: description: - Enable/disable logging. + type: str choices: - disable - enable - log-packet: + log_packet: description: - Enable/disable packet logging. + type: str choices: - disable - enable metadata: description: - Meta data. + type: list suboptions: id: description: - ID. required: true + type: int metaid: description: - Meta ID. + type: int valueid: description: - Value ID. + type: int name: description: - Rule name. required: true + type: str os: description: - Vulnerable operation systems. + type: str rev: description: - Revision. - rule-id: + type: int + rule_id: description: - Rule ID. + type: int service: description: - Vulnerable service. + type: str severity: description: - Severity. + type: str status: description: - Enable/disable status. + type: str choices: - disable - enable @@ -156,6 +184,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPS rules. fortios_ips_rule: @@ -164,15 +193,15 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" ips_rule: - state: "present" action: "pass" application: "" date: "5" group: "" location: "" log: "disable" - log-packet: "disable" + log_packet: "disable" metadata: - id: "11" @@ -181,7 +210,7 @@ EXAMPLES = ''' name: "default_name_14" os: "" rev: "16" - rule-id: "17" + rule_id: "17" service: "" severity: "" status: "disable" @@ -247,14 +276,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -262,14 +293,14 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_ips_rule_data(json): option_list = ['action', 'application', 'date', 'group', 'location', 'log', - 'log-packet', 'metadata', 'name', - 'os', 'rev', 'rule-id', + 'log_packet', 'metadata', 'name', + 'os', 'rev', 'rule_id', 'service', 'severity', 'status'] dictionary = {} @@ -280,48 +311,66 @@ def filter_ips_rule_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def ips_rule(data, fos): vdom = data['vdom'] + state = data['state'] ips_rule_data = data['ips_rule'] - filtered_data = filter_ips_rule_data(ips_rule_data) - if ips_rule_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_ips_rule_data(ips_rule_data)) + + if state == "present": return fos.set('ips', 'rule', data=filtered_data, vdom=vdom) - elif ips_rule_data['state'] == "absent": + elif state == "absent": return fos.delete('ips', 'rule', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_ips(data, fos): - login(data) - methodlist = ['ips_rule'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['ips_rule']: + resp = ips_rule(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "ips_rule": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "action": {"required": False, "type": "str", "choices": ["pass", "block"]}, "application": {"required": False, "type": "str"}, @@ -330,7 +379,7 @@ def main(): "location": {"required": False, "type": "str"}, "log": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "log-packet": {"required": False, "type": "str", + "log_packet": {"required": False, "type": "str", "choices": ["disable", "enable"]}, "metadata": {"required": False, "type": "list", "options": { @@ -341,7 +390,7 @@ def main(): "name": {"required": True, "type": "str"}, "os": {"required": False, "type": "str"}, "rev": {"required": False, "type": "int"}, - "rule-id": {"required": False, "type": "int"}, + "rule_id": {"required": False, "type": "int"}, "service": {"required": False, "type": "str"}, "severity": {"required": False, "type": "str"}, "status": {"required": False, "type": "str", @@ -353,15 +402,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_ips(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_ips(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_ips(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_ips_rule_settings.py b/lib/ansible/modules/network/fortios/fortios_ips_rule_settings.py index 6c835f049f9..4e6e55c0a20 100644 --- a/lib/ansible/modules/network/fortios/fortios_ips_rule_settings.py +++ b/lib/ansible/modules/network/fortios/fortios_ips_rule_settings.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_ips_rule_settings short_description: Configure IPS rule setting in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure ips feature and rule_settings category. - Examples includes all options and need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the + user to set and modify ips feature and rule_settings category. + Examples include all parameters and values need to be adjusted to datasources before usage. + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,44 +41,58 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip adress. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 ips_rule_settings: description: - Configure IPS rule setting. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent id: description: - Rule ID. required: true + type: int ''' EXAMPLES = ''' @@ -91,6 +102,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPS rule setting. fortios_ips_rule_settings: @@ -99,8 +111,8 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" ips_rule_settings: - state: "present" id: "3" ''' @@ -164,14 +176,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -179,7 +193,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_ips_rule_settings_data(json): @@ -193,48 +207,66 @@ def filter_ips_rule_settings_data(json): return dictionary +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data + + return data + + def ips_rule_settings(data, fos): vdom = data['vdom'] + state = data['state'] ips_rule_settings_data = data['ips_rule_settings'] - filtered_data = filter_ips_rule_settings_data(ips_rule_settings_data) - if ips_rule_settings_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_ips_rule_settings_data(ips_rule_settings_data)) + + if state == "present": return fos.set('ips', 'rule-settings', data=filtered_data, vdom=vdom) - elif ips_rule_settings_data['state'] == "absent": + elif state == "absent": return fos.delete('ips', 'rule-settings', mkey=filtered_data['id'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_ips(data, fos): - login(data) - methodlist = ['ips_rule_settings'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['ips_rule_settings']: + resp = ips_rule_settings(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "ips_rule_settings": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "id": {"required": True, "type": "int"} } @@ -243,15 +275,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_ips(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_ips(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_ips(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index 2613efb9f21..ebae76d439b 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -3693,38 +3693,6 @@ lib/ansible/modules/network/fortios/fortios_firewall_DoS_policy.py validate-modu lib/ansible/modules/network/fortios/fortios_firewall_DoS_policy6.py validate-modules:E336 lib/ansible/modules/network/fortios/fortios_firewall_policy.py validate-modules:E326 lib/ansible/modules/network/fortios/fortios_firewall_sniffer.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_firewall_ssl_ssh_profile.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_firewall_ssl_ssh_profile.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_firewall_ttl_policy.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_firewall_vip.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_firewall_vip.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_firewall_vip46.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_firewall_vip46.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_firewall_vip6.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_firewall_vip6.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_firewall_vip64.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_firewall_vip64.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_firewall_vipgrp.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_firewall_vipgrp46.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_firewall_vipgrp6.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_firewall_vipgrp64.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_firewall_wildcard_fqdn_custom.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_firewall_wildcard_fqdn_custom.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_firewall_wildcard_fqdn_group.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_ftp_proxy_explicit.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_ftp_proxy_explicit.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_icap_profile.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_icap_profile.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_icap_server.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_icap_server.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_ips_custom.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_ips_custom.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_ips_decoder.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_ips_global.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_ips_global.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_ips_rule.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_ips_rule.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_ips_rule_settings.py validate-modules:E337 lib/ansible/modules/network/fortios/fortios_ips_sensor.py validate-modules:E336 lib/ansible/modules/network/fortios/fortios_ips_sensor.py validate-modules:E337 lib/ansible/modules/network/fortios/fortios_ips_settings.py validate-modules:E336 diff --git a/test/units/modules/network/fortios/test_fortios_firewall_ssl_ssh_profile.py b/test/units/modules/network/fortios/test_fortios_firewall_ssl_ssh_profile.py new file mode 100644 index 00000000000..02a2b051ad2 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_firewall_ssl_ssh_profile.py @@ -0,0 +1,309 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_firewall_ssl_ssh_profile +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_firewall_ssl_ssh_profile.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_firewall_ssl_ssh_profile_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_ssl_ssh_profile': { + 'caname': 'test_value_3', + 'comment': 'Optional comments.', + 'mapi_over_https': 'enable', + 'name': 'default_name_6', + 'rpc_over_https': 'enable', + 'server_cert': 'test_value_8', + 'server_cert_mode': 're-sign', + 'ssl_anomalies_log': 'disable', + 'ssl_exemptions_log': 'disable', + 'untrusted_caname': 'test_value_12', + 'use_ssl_server': 'disable', + 'whitelist': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_ssl_ssh_profile.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'caname': 'test_value_3', + 'comment': 'Optional comments.', + 'mapi-over-https': 'enable', + 'name': 'default_name_6', + 'rpc-over-https': 'enable', + 'server-cert': 'test_value_8', + 'server-cert-mode': 're-sign', + 'ssl-anomalies-log': 'disable', + 'ssl-exemptions-log': 'disable', + 'untrusted-caname': 'test_value_12', + 'use-ssl-server': 'disable', + 'whitelist': 'enable' + } + + set_method_mock.assert_called_with('firewall', 'ssl-ssh-profile', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_ssl_ssh_profile_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_ssl_ssh_profile': { + 'caname': 'test_value_3', + 'comment': 'Optional comments.', + 'mapi_over_https': 'enable', + 'name': 'default_name_6', + 'rpc_over_https': 'enable', + 'server_cert': 'test_value_8', + 'server_cert_mode': 're-sign', + 'ssl_anomalies_log': 'disable', + 'ssl_exemptions_log': 'disable', + 'untrusted_caname': 'test_value_12', + 'use_ssl_server': 'disable', + 'whitelist': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_ssl_ssh_profile.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'caname': 'test_value_3', + 'comment': 'Optional comments.', + 'mapi-over-https': 'enable', + 'name': 'default_name_6', + 'rpc-over-https': 'enable', + 'server-cert': 'test_value_8', + 'server-cert-mode': 're-sign', + 'ssl-anomalies-log': 'disable', + 'ssl-exemptions-log': 'disable', + 'untrusted-caname': 'test_value_12', + 'use-ssl-server': 'disable', + 'whitelist': 'enable' + } + + set_method_mock.assert_called_with('firewall', 'ssl-ssh-profile', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_ssl_ssh_profile_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_ssl_ssh_profile': { + 'caname': 'test_value_3', + 'comment': 'Optional comments.', + 'mapi_over_https': 'enable', + 'name': 'default_name_6', + 'rpc_over_https': 'enable', + 'server_cert': 'test_value_8', + 'server_cert_mode': 're-sign', + 'ssl_anomalies_log': 'disable', + 'ssl_exemptions_log': 'disable', + 'untrusted_caname': 'test_value_12', + 'use_ssl_server': 'disable', + 'whitelist': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_ssl_ssh_profile.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'ssl-ssh-profile', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_ssl_ssh_profile_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_ssl_ssh_profile': { + 'caname': 'test_value_3', + 'comment': 'Optional comments.', + 'mapi_over_https': 'enable', + 'name': 'default_name_6', + 'rpc_over_https': 'enable', + 'server_cert': 'test_value_8', + 'server_cert_mode': 're-sign', + 'ssl_anomalies_log': 'disable', + 'ssl_exemptions_log': 'disable', + 'untrusted_caname': 'test_value_12', + 'use_ssl_server': 'disable', + 'whitelist': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_ssl_ssh_profile.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'ssl-ssh-profile', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_ssl_ssh_profile_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_ssl_ssh_profile': { + 'caname': 'test_value_3', + 'comment': 'Optional comments.', + 'mapi_over_https': 'enable', + 'name': 'default_name_6', + 'rpc_over_https': 'enable', + 'server_cert': 'test_value_8', + 'server_cert_mode': 're-sign', + 'ssl_anomalies_log': 'disable', + 'ssl_exemptions_log': 'disable', + 'untrusted_caname': 'test_value_12', + 'use_ssl_server': 'disable', + 'whitelist': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_ssl_ssh_profile.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'caname': 'test_value_3', + 'comment': 'Optional comments.', + 'mapi-over-https': 'enable', + 'name': 'default_name_6', + 'rpc-over-https': 'enable', + 'server-cert': 'test_value_8', + 'server-cert-mode': 're-sign', + 'ssl-anomalies-log': 'disable', + 'ssl-exemptions-log': 'disable', + 'untrusted-caname': 'test_value_12', + 'use-ssl-server': 'disable', + 'whitelist': 'enable' + } + + set_method_mock.assert_called_with('firewall', 'ssl-ssh-profile', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_firewall_ssl_ssh_profile_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_ssl_ssh_profile': { + 'random_attribute_not_valid': 'tag', + 'caname': 'test_value_3', + 'comment': 'Optional comments.', + 'mapi_over_https': 'enable', + 'name': 'default_name_6', + 'rpc_over_https': 'enable', + 'server_cert': 'test_value_8', + 'server_cert_mode': 're-sign', + 'ssl_anomalies_log': 'disable', + 'ssl_exemptions_log': 'disable', + 'untrusted_caname': 'test_value_12', + 'use_ssl_server': 'disable', + 'whitelist': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_ssl_ssh_profile.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'caname': 'test_value_3', + 'comment': 'Optional comments.', + 'mapi-over-https': 'enable', + 'name': 'default_name_6', + 'rpc-over-https': 'enable', + 'server-cert': 'test_value_8', + 'server-cert-mode': 're-sign', + 'ssl-anomalies-log': 'disable', + 'ssl-exemptions-log': 'disable', + 'untrusted-caname': 'test_value_12', + 'use-ssl-server': 'disable', + 'whitelist': 'enable' + } + + set_method_mock.assert_called_with('firewall', 'ssl-ssh-profile', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_firewall_ttl_policy.py b/test/units/modules/network/fortios/test_fortios_firewall_ttl_policy.py new file mode 100644 index 00000000000..792fb758770 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_firewall_ttl_policy.py @@ -0,0 +1,249 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_firewall_ttl_policy +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_firewall_ttl_policy.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_firewall_ttl_policy_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_ttl_policy': { + 'action': 'accept', + 'id': '4', + 'schedule': 'test_value_5', + 'srcintf': 'test_value_6', + 'status': 'enable', + 'ttl': 'test_value_8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_ttl_policy.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'action': 'accept', + 'id': '4', + 'schedule': 'test_value_5', + 'srcintf': 'test_value_6', + 'status': 'enable', + 'ttl': 'test_value_8' + } + + set_method_mock.assert_called_with('firewall', 'ttl-policy', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_ttl_policy_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_ttl_policy': { + 'action': 'accept', + 'id': '4', + 'schedule': 'test_value_5', + 'srcintf': 'test_value_6', + 'status': 'enable', + 'ttl': 'test_value_8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_ttl_policy.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'action': 'accept', + 'id': '4', + 'schedule': 'test_value_5', + 'srcintf': 'test_value_6', + 'status': 'enable', + 'ttl': 'test_value_8' + } + + set_method_mock.assert_called_with('firewall', 'ttl-policy', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_ttl_policy_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_ttl_policy': { + 'action': 'accept', + 'id': '4', + 'schedule': 'test_value_5', + 'srcintf': 'test_value_6', + 'status': 'enable', + 'ttl': 'test_value_8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_ttl_policy.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'ttl-policy', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_ttl_policy_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_ttl_policy': { + 'action': 'accept', + 'id': '4', + 'schedule': 'test_value_5', + 'srcintf': 'test_value_6', + 'status': 'enable', + 'ttl': 'test_value_8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_ttl_policy.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'ttl-policy', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_ttl_policy_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_ttl_policy': { + 'action': 'accept', + 'id': '4', + 'schedule': 'test_value_5', + 'srcintf': 'test_value_6', + 'status': 'enable', + 'ttl': 'test_value_8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_ttl_policy.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'action': 'accept', + 'id': '4', + 'schedule': 'test_value_5', + 'srcintf': 'test_value_6', + 'status': 'enable', + 'ttl': 'test_value_8' + } + + set_method_mock.assert_called_with('firewall', 'ttl-policy', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_firewall_ttl_policy_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_ttl_policy': { + 'random_attribute_not_valid': 'tag', + 'action': 'accept', + 'id': '4', + 'schedule': 'test_value_5', + 'srcintf': 'test_value_6', + 'status': 'enable', + 'ttl': 'test_value_8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_ttl_policy.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'action': 'accept', + 'id': '4', + 'schedule': 'test_value_5', + 'srcintf': 'test_value_6', + 'status': 'enable', + 'ttl': 'test_value_8' + } + + set_method_mock.assert_called_with('firewall', 'ttl-policy', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_firewall_vip.py b/test/units/modules/network/fortios/test_fortios_firewall_vip.py new file mode 100644 index 00000000000..a24bdd6b8f3 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_firewall_vip.py @@ -0,0 +1,839 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_firewall_vip +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_firewall_vip.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_firewall_vip_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'dns_mapping_ttl': '6', + 'extintf': 'test_value_7', + 'extip': 'test_value_8', + 'extport': 'test_value_9', + 'gratuitous_arp_interval': '10', + 'http_cookie_age': '11', + 'http_cookie_domain': 'test_value_12', + 'http_cookie_domain_from_host': 'disable', + 'http_cookie_generation': '14', + 'http_cookie_path': 'test_value_15', + 'http_cookie_share': 'disable', + 'http_ip_header': 'enable', + 'http_ip_header_name': 'test_value_18', + 'http_multiplex': 'enable', + 'https_cookie_secure': 'disable', + 'id': '21', + 'ldb_method': 'static', + 'mapped_addr': 'test_value_23', + 'mappedport': 'test_value_24', + 'max_embryonic_connections': '25', + 'name': 'default_name_26', + 'nat_source_vip': 'disable', + 'outlook_web_access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'portmapping_type': '1-to-1', + 'protocol': 'tcp', + 'server_type': 'http', + 'ssl_algorithm': 'high', + 'ssl_certificate': 'test_value_35', + 'ssl_client_fallback': 'disable', + 'ssl_client_renegotiation': 'allow', + 'ssl_client_session_state_max': '38', + 'ssl_client_session_state_timeout': '39', + 'ssl_client_session_state_type': 'disable', + 'ssl_dh_bits': '768', + 'ssl_hpkp': 'disable', + 'ssl_hpkp_age': '43', + 'ssl_hpkp_backup': 'test_value_44', + 'ssl_hpkp_include_subdomains': 'disable', + 'ssl_hpkp_primary': 'test_value_46', + 'ssl_hpkp_report_uri': 'test_value_47', + 'ssl_hsts': 'disable', + 'ssl_hsts_age': '49', + 'ssl_hsts_include_subdomains': 'disable', + 'ssl_http_location_conversion': 'enable', + 'ssl_http_match_host': 'enable', + 'ssl_max_version': 'ssl-3.0', + 'ssl_min_version': 'ssl-3.0', + 'ssl_mode': 'half', + 'ssl_pfs': 'require', + 'ssl_send_empty_frags': 'enable', + 'ssl_server_algorithm': 'high', + 'ssl_server_max_version': 'ssl-3.0', + 'ssl_server_min_version': 'ssl-3.0', + 'ssl_server_session_state_max': '61', + 'ssl_server_session_state_timeout': '62', + 'ssl_server_session_state_type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_65', + 'weblogic_server': 'disable', + 'websphere_server': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'dns-mapping-ttl': '6', + 'extintf': 'test_value_7', + 'extip': 'test_value_8', + 'extport': 'test_value_9', + 'gratuitous-arp-interval': '10', + 'http-cookie-age': '11', + 'http-cookie-domain': 'test_value_12', + 'http-cookie-domain-from-host': 'disable', + 'http-cookie-generation': '14', + 'http-cookie-path': 'test_value_15', + 'http-cookie-share': 'disable', + 'http-ip-header': 'enable', + 'http-ip-header-name': 'test_value_18', + 'http-multiplex': 'enable', + 'https-cookie-secure': 'disable', + 'id': '21', + 'ldb-method': 'static', + 'mapped-addr': 'test_value_23', + 'mappedport': 'test_value_24', + 'max-embryonic-connections': '25', + 'name': 'default_name_26', + 'nat-source-vip': 'disable', + 'outlook-web-access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'portmapping-type': '1-to-1', + 'protocol': 'tcp', + 'server-type': 'http', + 'ssl-algorithm': 'high', + 'ssl-certificate': 'test_value_35', + 'ssl-client-fallback': 'disable', + 'ssl-client-renegotiation': 'allow', + 'ssl-client-session-state-max': '38', + 'ssl-client-session-state-timeout': '39', + 'ssl-client-session-state-type': 'disable', + 'ssl-dh-bits': '768', + 'ssl-hpkp': 'disable', + 'ssl-hpkp-age': '43', + 'ssl-hpkp-backup': 'test_value_44', + 'ssl-hpkp-include-subdomains': 'disable', + 'ssl-hpkp-primary': 'test_value_46', + 'ssl-hpkp-report-uri': 'test_value_47', + 'ssl-hsts': 'disable', + 'ssl-hsts-age': '49', + 'ssl-hsts-include-subdomains': 'disable', + 'ssl-http-location-conversion': 'enable', + 'ssl-http-match-host': 'enable', + 'ssl-max-version': 'ssl-3.0', + 'ssl-min-version': 'ssl-3.0', + 'ssl-mode': 'half', + 'ssl-pfs': 'require', + 'ssl-send-empty-frags': 'enable', + 'ssl-server-algorithm': 'high', + 'ssl-server-max-version': 'ssl-3.0', + 'ssl-server-min-version': 'ssl-3.0', + 'ssl-server-session-state-max': '61', + 'ssl-server-session-state-timeout': '62', + 'ssl-server-session-state-type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_65', + 'weblogic-server': 'disable', + 'websphere-server': 'disable' + } + + set_method_mock.assert_called_with('firewall', 'vip', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vip_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'dns_mapping_ttl': '6', + 'extintf': 'test_value_7', + 'extip': 'test_value_8', + 'extport': 'test_value_9', + 'gratuitous_arp_interval': '10', + 'http_cookie_age': '11', + 'http_cookie_domain': 'test_value_12', + 'http_cookie_domain_from_host': 'disable', + 'http_cookie_generation': '14', + 'http_cookie_path': 'test_value_15', + 'http_cookie_share': 'disable', + 'http_ip_header': 'enable', + 'http_ip_header_name': 'test_value_18', + 'http_multiplex': 'enable', + 'https_cookie_secure': 'disable', + 'id': '21', + 'ldb_method': 'static', + 'mapped_addr': 'test_value_23', + 'mappedport': 'test_value_24', + 'max_embryonic_connections': '25', + 'name': 'default_name_26', + 'nat_source_vip': 'disable', + 'outlook_web_access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'portmapping_type': '1-to-1', + 'protocol': 'tcp', + 'server_type': 'http', + 'ssl_algorithm': 'high', + 'ssl_certificate': 'test_value_35', + 'ssl_client_fallback': 'disable', + 'ssl_client_renegotiation': 'allow', + 'ssl_client_session_state_max': '38', + 'ssl_client_session_state_timeout': '39', + 'ssl_client_session_state_type': 'disable', + 'ssl_dh_bits': '768', + 'ssl_hpkp': 'disable', + 'ssl_hpkp_age': '43', + 'ssl_hpkp_backup': 'test_value_44', + 'ssl_hpkp_include_subdomains': 'disable', + 'ssl_hpkp_primary': 'test_value_46', + 'ssl_hpkp_report_uri': 'test_value_47', + 'ssl_hsts': 'disable', + 'ssl_hsts_age': '49', + 'ssl_hsts_include_subdomains': 'disable', + 'ssl_http_location_conversion': 'enable', + 'ssl_http_match_host': 'enable', + 'ssl_max_version': 'ssl-3.0', + 'ssl_min_version': 'ssl-3.0', + 'ssl_mode': 'half', + 'ssl_pfs': 'require', + 'ssl_send_empty_frags': 'enable', + 'ssl_server_algorithm': 'high', + 'ssl_server_max_version': 'ssl-3.0', + 'ssl_server_min_version': 'ssl-3.0', + 'ssl_server_session_state_max': '61', + 'ssl_server_session_state_timeout': '62', + 'ssl_server_session_state_type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_65', + 'weblogic_server': 'disable', + 'websphere_server': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'dns-mapping-ttl': '6', + 'extintf': 'test_value_7', + 'extip': 'test_value_8', + 'extport': 'test_value_9', + 'gratuitous-arp-interval': '10', + 'http-cookie-age': '11', + 'http-cookie-domain': 'test_value_12', + 'http-cookie-domain-from-host': 'disable', + 'http-cookie-generation': '14', + 'http-cookie-path': 'test_value_15', + 'http-cookie-share': 'disable', + 'http-ip-header': 'enable', + 'http-ip-header-name': 'test_value_18', + 'http-multiplex': 'enable', + 'https-cookie-secure': 'disable', + 'id': '21', + 'ldb-method': 'static', + 'mapped-addr': 'test_value_23', + 'mappedport': 'test_value_24', + 'max-embryonic-connections': '25', + 'name': 'default_name_26', + 'nat-source-vip': 'disable', + 'outlook-web-access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'portmapping-type': '1-to-1', + 'protocol': 'tcp', + 'server-type': 'http', + 'ssl-algorithm': 'high', + 'ssl-certificate': 'test_value_35', + 'ssl-client-fallback': 'disable', + 'ssl-client-renegotiation': 'allow', + 'ssl-client-session-state-max': '38', + 'ssl-client-session-state-timeout': '39', + 'ssl-client-session-state-type': 'disable', + 'ssl-dh-bits': '768', + 'ssl-hpkp': 'disable', + 'ssl-hpkp-age': '43', + 'ssl-hpkp-backup': 'test_value_44', + 'ssl-hpkp-include-subdomains': 'disable', + 'ssl-hpkp-primary': 'test_value_46', + 'ssl-hpkp-report-uri': 'test_value_47', + 'ssl-hsts': 'disable', + 'ssl-hsts-age': '49', + 'ssl-hsts-include-subdomains': 'disable', + 'ssl-http-location-conversion': 'enable', + 'ssl-http-match-host': 'enable', + 'ssl-max-version': 'ssl-3.0', + 'ssl-min-version': 'ssl-3.0', + 'ssl-mode': 'half', + 'ssl-pfs': 'require', + 'ssl-send-empty-frags': 'enable', + 'ssl-server-algorithm': 'high', + 'ssl-server-max-version': 'ssl-3.0', + 'ssl-server-min-version': 'ssl-3.0', + 'ssl-server-session-state-max': '61', + 'ssl-server-session-state-timeout': '62', + 'ssl-server-session-state-type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_65', + 'weblogic-server': 'disable', + 'websphere-server': 'disable' + } + + set_method_mock.assert_called_with('firewall', 'vip', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vip_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vip': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'dns_mapping_ttl': '6', + 'extintf': 'test_value_7', + 'extip': 'test_value_8', + 'extport': 'test_value_9', + 'gratuitous_arp_interval': '10', + 'http_cookie_age': '11', + 'http_cookie_domain': 'test_value_12', + 'http_cookie_domain_from_host': 'disable', + 'http_cookie_generation': '14', + 'http_cookie_path': 'test_value_15', + 'http_cookie_share': 'disable', + 'http_ip_header': 'enable', + 'http_ip_header_name': 'test_value_18', + 'http_multiplex': 'enable', + 'https_cookie_secure': 'disable', + 'id': '21', + 'ldb_method': 'static', + 'mapped_addr': 'test_value_23', + 'mappedport': 'test_value_24', + 'max_embryonic_connections': '25', + 'name': 'default_name_26', + 'nat_source_vip': 'disable', + 'outlook_web_access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'portmapping_type': '1-to-1', + 'protocol': 'tcp', + 'server_type': 'http', + 'ssl_algorithm': 'high', + 'ssl_certificate': 'test_value_35', + 'ssl_client_fallback': 'disable', + 'ssl_client_renegotiation': 'allow', + 'ssl_client_session_state_max': '38', + 'ssl_client_session_state_timeout': '39', + 'ssl_client_session_state_type': 'disable', + 'ssl_dh_bits': '768', + 'ssl_hpkp': 'disable', + 'ssl_hpkp_age': '43', + 'ssl_hpkp_backup': 'test_value_44', + 'ssl_hpkp_include_subdomains': 'disable', + 'ssl_hpkp_primary': 'test_value_46', + 'ssl_hpkp_report_uri': 'test_value_47', + 'ssl_hsts': 'disable', + 'ssl_hsts_age': '49', + 'ssl_hsts_include_subdomains': 'disable', + 'ssl_http_location_conversion': 'enable', + 'ssl_http_match_host': 'enable', + 'ssl_max_version': 'ssl-3.0', + 'ssl_min_version': 'ssl-3.0', + 'ssl_mode': 'half', + 'ssl_pfs': 'require', + 'ssl_send_empty_frags': 'enable', + 'ssl_server_algorithm': 'high', + 'ssl_server_max_version': 'ssl-3.0', + 'ssl_server_min_version': 'ssl-3.0', + 'ssl_server_session_state_max': '61', + 'ssl_server_session_state_timeout': '62', + 'ssl_server_session_state_type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_65', + 'weblogic_server': 'disable', + 'websphere_server': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vip', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vip_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vip': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'dns_mapping_ttl': '6', + 'extintf': 'test_value_7', + 'extip': 'test_value_8', + 'extport': 'test_value_9', + 'gratuitous_arp_interval': '10', + 'http_cookie_age': '11', + 'http_cookie_domain': 'test_value_12', + 'http_cookie_domain_from_host': 'disable', + 'http_cookie_generation': '14', + 'http_cookie_path': 'test_value_15', + 'http_cookie_share': 'disable', + 'http_ip_header': 'enable', + 'http_ip_header_name': 'test_value_18', + 'http_multiplex': 'enable', + 'https_cookie_secure': 'disable', + 'id': '21', + 'ldb_method': 'static', + 'mapped_addr': 'test_value_23', + 'mappedport': 'test_value_24', + 'max_embryonic_connections': '25', + 'name': 'default_name_26', + 'nat_source_vip': 'disable', + 'outlook_web_access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'portmapping_type': '1-to-1', + 'protocol': 'tcp', + 'server_type': 'http', + 'ssl_algorithm': 'high', + 'ssl_certificate': 'test_value_35', + 'ssl_client_fallback': 'disable', + 'ssl_client_renegotiation': 'allow', + 'ssl_client_session_state_max': '38', + 'ssl_client_session_state_timeout': '39', + 'ssl_client_session_state_type': 'disable', + 'ssl_dh_bits': '768', + 'ssl_hpkp': 'disable', + 'ssl_hpkp_age': '43', + 'ssl_hpkp_backup': 'test_value_44', + 'ssl_hpkp_include_subdomains': 'disable', + 'ssl_hpkp_primary': 'test_value_46', + 'ssl_hpkp_report_uri': 'test_value_47', + 'ssl_hsts': 'disable', + 'ssl_hsts_age': '49', + 'ssl_hsts_include_subdomains': 'disable', + 'ssl_http_location_conversion': 'enable', + 'ssl_http_match_host': 'enable', + 'ssl_max_version': 'ssl-3.0', + 'ssl_min_version': 'ssl-3.0', + 'ssl_mode': 'half', + 'ssl_pfs': 'require', + 'ssl_send_empty_frags': 'enable', + 'ssl_server_algorithm': 'high', + 'ssl_server_max_version': 'ssl-3.0', + 'ssl_server_min_version': 'ssl-3.0', + 'ssl_server_session_state_max': '61', + 'ssl_server_session_state_timeout': '62', + 'ssl_server_session_state_type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_65', + 'weblogic_server': 'disable', + 'websphere_server': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vip', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vip_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'dns_mapping_ttl': '6', + 'extintf': 'test_value_7', + 'extip': 'test_value_8', + 'extport': 'test_value_9', + 'gratuitous_arp_interval': '10', + 'http_cookie_age': '11', + 'http_cookie_domain': 'test_value_12', + 'http_cookie_domain_from_host': 'disable', + 'http_cookie_generation': '14', + 'http_cookie_path': 'test_value_15', + 'http_cookie_share': 'disable', + 'http_ip_header': 'enable', + 'http_ip_header_name': 'test_value_18', + 'http_multiplex': 'enable', + 'https_cookie_secure': 'disable', + 'id': '21', + 'ldb_method': 'static', + 'mapped_addr': 'test_value_23', + 'mappedport': 'test_value_24', + 'max_embryonic_connections': '25', + 'name': 'default_name_26', + 'nat_source_vip': 'disable', + 'outlook_web_access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'portmapping_type': '1-to-1', + 'protocol': 'tcp', + 'server_type': 'http', + 'ssl_algorithm': 'high', + 'ssl_certificate': 'test_value_35', + 'ssl_client_fallback': 'disable', + 'ssl_client_renegotiation': 'allow', + 'ssl_client_session_state_max': '38', + 'ssl_client_session_state_timeout': '39', + 'ssl_client_session_state_type': 'disable', + 'ssl_dh_bits': '768', + 'ssl_hpkp': 'disable', + 'ssl_hpkp_age': '43', + 'ssl_hpkp_backup': 'test_value_44', + 'ssl_hpkp_include_subdomains': 'disable', + 'ssl_hpkp_primary': 'test_value_46', + 'ssl_hpkp_report_uri': 'test_value_47', + 'ssl_hsts': 'disable', + 'ssl_hsts_age': '49', + 'ssl_hsts_include_subdomains': 'disable', + 'ssl_http_location_conversion': 'enable', + 'ssl_http_match_host': 'enable', + 'ssl_max_version': 'ssl-3.0', + 'ssl_min_version': 'ssl-3.0', + 'ssl_mode': 'half', + 'ssl_pfs': 'require', + 'ssl_send_empty_frags': 'enable', + 'ssl_server_algorithm': 'high', + 'ssl_server_max_version': 'ssl-3.0', + 'ssl_server_min_version': 'ssl-3.0', + 'ssl_server_session_state_max': '61', + 'ssl_server_session_state_timeout': '62', + 'ssl_server_session_state_type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_65', + 'weblogic_server': 'disable', + 'websphere_server': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'dns-mapping-ttl': '6', + 'extintf': 'test_value_7', + 'extip': 'test_value_8', + 'extport': 'test_value_9', + 'gratuitous-arp-interval': '10', + 'http-cookie-age': '11', + 'http-cookie-domain': 'test_value_12', + 'http-cookie-domain-from-host': 'disable', + 'http-cookie-generation': '14', + 'http-cookie-path': 'test_value_15', + 'http-cookie-share': 'disable', + 'http-ip-header': 'enable', + 'http-ip-header-name': 'test_value_18', + 'http-multiplex': 'enable', + 'https-cookie-secure': 'disable', + 'id': '21', + 'ldb-method': 'static', + 'mapped-addr': 'test_value_23', + 'mappedport': 'test_value_24', + 'max-embryonic-connections': '25', + 'name': 'default_name_26', + 'nat-source-vip': 'disable', + 'outlook-web-access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'portmapping-type': '1-to-1', + 'protocol': 'tcp', + 'server-type': 'http', + 'ssl-algorithm': 'high', + 'ssl-certificate': 'test_value_35', + 'ssl-client-fallback': 'disable', + 'ssl-client-renegotiation': 'allow', + 'ssl-client-session-state-max': '38', + 'ssl-client-session-state-timeout': '39', + 'ssl-client-session-state-type': 'disable', + 'ssl-dh-bits': '768', + 'ssl-hpkp': 'disable', + 'ssl-hpkp-age': '43', + 'ssl-hpkp-backup': 'test_value_44', + 'ssl-hpkp-include-subdomains': 'disable', + 'ssl-hpkp-primary': 'test_value_46', + 'ssl-hpkp-report-uri': 'test_value_47', + 'ssl-hsts': 'disable', + 'ssl-hsts-age': '49', + 'ssl-hsts-include-subdomains': 'disable', + 'ssl-http-location-conversion': 'enable', + 'ssl-http-match-host': 'enable', + 'ssl-max-version': 'ssl-3.0', + 'ssl-min-version': 'ssl-3.0', + 'ssl-mode': 'half', + 'ssl-pfs': 'require', + 'ssl-send-empty-frags': 'enable', + 'ssl-server-algorithm': 'high', + 'ssl-server-max-version': 'ssl-3.0', + 'ssl-server-min-version': 'ssl-3.0', + 'ssl-server-session-state-max': '61', + 'ssl-server-session-state-timeout': '62', + 'ssl-server-session-state-type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_65', + 'weblogic-server': 'disable', + 'websphere-server': 'disable' + } + + set_method_mock.assert_called_with('firewall', 'vip', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_firewall_vip_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip': { + 'random_attribute_not_valid': 'tag', + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'dns_mapping_ttl': '6', + 'extintf': 'test_value_7', + 'extip': 'test_value_8', + 'extport': 'test_value_9', + 'gratuitous_arp_interval': '10', + 'http_cookie_age': '11', + 'http_cookie_domain': 'test_value_12', + 'http_cookie_domain_from_host': 'disable', + 'http_cookie_generation': '14', + 'http_cookie_path': 'test_value_15', + 'http_cookie_share': 'disable', + 'http_ip_header': 'enable', + 'http_ip_header_name': 'test_value_18', + 'http_multiplex': 'enable', + 'https_cookie_secure': 'disable', + 'id': '21', + 'ldb_method': 'static', + 'mapped_addr': 'test_value_23', + 'mappedport': 'test_value_24', + 'max_embryonic_connections': '25', + 'name': 'default_name_26', + 'nat_source_vip': 'disable', + 'outlook_web_access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'portmapping_type': '1-to-1', + 'protocol': 'tcp', + 'server_type': 'http', + 'ssl_algorithm': 'high', + 'ssl_certificate': 'test_value_35', + 'ssl_client_fallback': 'disable', + 'ssl_client_renegotiation': 'allow', + 'ssl_client_session_state_max': '38', + 'ssl_client_session_state_timeout': '39', + 'ssl_client_session_state_type': 'disable', + 'ssl_dh_bits': '768', + 'ssl_hpkp': 'disable', + 'ssl_hpkp_age': '43', + 'ssl_hpkp_backup': 'test_value_44', + 'ssl_hpkp_include_subdomains': 'disable', + 'ssl_hpkp_primary': 'test_value_46', + 'ssl_hpkp_report_uri': 'test_value_47', + 'ssl_hsts': 'disable', + 'ssl_hsts_age': '49', + 'ssl_hsts_include_subdomains': 'disable', + 'ssl_http_location_conversion': 'enable', + 'ssl_http_match_host': 'enable', + 'ssl_max_version': 'ssl-3.0', + 'ssl_min_version': 'ssl-3.0', + 'ssl_mode': 'half', + 'ssl_pfs': 'require', + 'ssl_send_empty_frags': 'enable', + 'ssl_server_algorithm': 'high', + 'ssl_server_max_version': 'ssl-3.0', + 'ssl_server_min_version': 'ssl-3.0', + 'ssl_server_session_state_max': '61', + 'ssl_server_session_state_timeout': '62', + 'ssl_server_session_state_type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_65', + 'weblogic_server': 'disable', + 'websphere_server': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'dns-mapping-ttl': '6', + 'extintf': 'test_value_7', + 'extip': 'test_value_8', + 'extport': 'test_value_9', + 'gratuitous-arp-interval': '10', + 'http-cookie-age': '11', + 'http-cookie-domain': 'test_value_12', + 'http-cookie-domain-from-host': 'disable', + 'http-cookie-generation': '14', + 'http-cookie-path': 'test_value_15', + 'http-cookie-share': 'disable', + 'http-ip-header': 'enable', + 'http-ip-header-name': 'test_value_18', + 'http-multiplex': 'enable', + 'https-cookie-secure': 'disable', + 'id': '21', + 'ldb-method': 'static', + 'mapped-addr': 'test_value_23', + 'mappedport': 'test_value_24', + 'max-embryonic-connections': '25', + 'name': 'default_name_26', + 'nat-source-vip': 'disable', + 'outlook-web-access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'portmapping-type': '1-to-1', + 'protocol': 'tcp', + 'server-type': 'http', + 'ssl-algorithm': 'high', + 'ssl-certificate': 'test_value_35', + 'ssl-client-fallback': 'disable', + 'ssl-client-renegotiation': 'allow', + 'ssl-client-session-state-max': '38', + 'ssl-client-session-state-timeout': '39', + 'ssl-client-session-state-type': 'disable', + 'ssl-dh-bits': '768', + 'ssl-hpkp': 'disable', + 'ssl-hpkp-age': '43', + 'ssl-hpkp-backup': 'test_value_44', + 'ssl-hpkp-include-subdomains': 'disable', + 'ssl-hpkp-primary': 'test_value_46', + 'ssl-hpkp-report-uri': 'test_value_47', + 'ssl-hsts': 'disable', + 'ssl-hsts-age': '49', + 'ssl-hsts-include-subdomains': 'disable', + 'ssl-http-location-conversion': 'enable', + 'ssl-http-match-host': 'enable', + 'ssl-max-version': 'ssl-3.0', + 'ssl-min-version': 'ssl-3.0', + 'ssl-mode': 'half', + 'ssl-pfs': 'require', + 'ssl-send-empty-frags': 'enable', + 'ssl-server-algorithm': 'high', + 'ssl-server-max-version': 'ssl-3.0', + 'ssl-server-min-version': 'ssl-3.0', + 'ssl-server-session-state-max': '61', + 'ssl-server-session-state-timeout': '62', + 'ssl-server-session-state-type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_65', + 'weblogic-server': 'disable', + 'websphere-server': 'disable' + } + + set_method_mock.assert_called_with('firewall', 'vip', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_firewall_vip46.py b/test/units/modules/network/fortios/test_fortios_firewall_vip46.py new file mode 100644 index 00000000000..dd8cacb67da --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_firewall_vip46.py @@ -0,0 +1,339 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_firewall_vip46 +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_firewall_vip46.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_firewall_vip46_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip46': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb_method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip46.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb-method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server-type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + } + + set_method_mock.assert_called_with('firewall', 'vip46', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vip46_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip46': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb_method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip46.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb-method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server-type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + } + + set_method_mock.assert_called_with('firewall', 'vip46', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vip46_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vip46': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb_method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip46.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vip46', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vip46_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vip46': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb_method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip46.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vip46', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vip46_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip46': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb_method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip46.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb-method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server-type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + } + + set_method_mock.assert_called_with('firewall', 'vip46', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_firewall_vip46_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip46': { + 'random_attribute_not_valid': 'tag', + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb_method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip46.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb-method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server-type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + } + + set_method_mock.assert_called_with('firewall', 'vip46', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_firewall_vip6.py b/test/units/modules/network/fortios/test_fortios_firewall_vip6.py new file mode 100644 index 00000000000..2d6aaf036c1 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_firewall_vip6.py @@ -0,0 +1,789 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_firewall_vip6 +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_firewall_vip6.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_firewall_vip6_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip6': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'http_cookie_age': '8', + 'http_cookie_domain': 'test_value_9', + 'http_cookie_domain_from_host': 'disable', + 'http_cookie_generation': '11', + 'http_cookie_path': 'test_value_12', + 'http_cookie_share': 'disable', + 'http_ip_header': 'enable', + 'http_ip_header_name': 'test_value_15', + 'http_multiplex': 'enable', + 'https_cookie_secure': 'disable', + 'id': '18', + 'ldb_method': 'static', + 'mappedip': 'test_value_20', + 'mappedport': 'test_value_21', + 'max_embryonic_connections': '22', + 'name': 'default_name_23', + 'outlook_web_access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'ssl_algorithm': 'high', + 'ssl_certificate': 'test_value_30', + 'ssl_client_fallback': 'disable', + 'ssl_client_renegotiation': 'allow', + 'ssl_client_session_state_max': '33', + 'ssl_client_session_state_timeout': '34', + 'ssl_client_session_state_type': 'disable', + 'ssl_dh_bits': '768', + 'ssl_hpkp': 'disable', + 'ssl_hpkp_age': '38', + 'ssl_hpkp_backup': 'test_value_39', + 'ssl_hpkp_include_subdomains': 'disable', + 'ssl_hpkp_primary': 'test_value_41', + 'ssl_hpkp_report_uri': 'test_value_42', + 'ssl_hsts': 'disable', + 'ssl_hsts_age': '44', + 'ssl_hsts_include_subdomains': 'disable', + 'ssl_http_location_conversion': 'enable', + 'ssl_http_match_host': 'enable', + 'ssl_max_version': 'ssl-3.0', + 'ssl_min_version': 'ssl-3.0', + 'ssl_mode': 'half', + 'ssl_pfs': 'require', + 'ssl_send_empty_frags': 'enable', + 'ssl_server_algorithm': 'high', + 'ssl_server_max_version': 'ssl-3.0', + 'ssl_server_min_version': 'ssl-3.0', + 'ssl_server_session_state_max': '56', + 'ssl_server_session_state_timeout': '57', + 'ssl_server_session_state_type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_60', + 'weblogic_server': 'disable', + 'websphere_server': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip6.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'http-cookie-age': '8', + 'http-cookie-domain': 'test_value_9', + 'http-cookie-domain-from-host': 'disable', + 'http-cookie-generation': '11', + 'http-cookie-path': 'test_value_12', + 'http-cookie-share': 'disable', + 'http-ip-header': 'enable', + 'http-ip-header-name': 'test_value_15', + 'http-multiplex': 'enable', + 'https-cookie-secure': 'disable', + 'id': '18', + 'ldb-method': 'static', + 'mappedip': 'test_value_20', + 'mappedport': 'test_value_21', + 'max-embryonic-connections': '22', + 'name': 'default_name_23', + 'outlook-web-access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server-type': 'http', + 'ssl-algorithm': 'high', + 'ssl-certificate': 'test_value_30', + 'ssl-client-fallback': 'disable', + 'ssl-client-renegotiation': 'allow', + 'ssl-client-session-state-max': '33', + 'ssl-client-session-state-timeout': '34', + 'ssl-client-session-state-type': 'disable', + 'ssl-dh-bits': '768', + 'ssl-hpkp': 'disable', + 'ssl-hpkp-age': '38', + 'ssl-hpkp-backup': 'test_value_39', + 'ssl-hpkp-include-subdomains': 'disable', + 'ssl-hpkp-primary': 'test_value_41', + 'ssl-hpkp-report-uri': 'test_value_42', + 'ssl-hsts': 'disable', + 'ssl-hsts-age': '44', + 'ssl-hsts-include-subdomains': 'disable', + 'ssl-http-location-conversion': 'enable', + 'ssl-http-match-host': 'enable', + 'ssl-max-version': 'ssl-3.0', + 'ssl-min-version': 'ssl-3.0', + 'ssl-mode': 'half', + 'ssl-pfs': 'require', + 'ssl-send-empty-frags': 'enable', + 'ssl-server-algorithm': 'high', + 'ssl-server-max-version': 'ssl-3.0', + 'ssl-server-min-version': 'ssl-3.0', + 'ssl-server-session-state-max': '56', + 'ssl-server-session-state-timeout': '57', + 'ssl-server-session-state-type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_60', + 'weblogic-server': 'disable', + 'websphere-server': 'disable' + } + + set_method_mock.assert_called_with('firewall', 'vip6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vip6_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip6': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'http_cookie_age': '8', + 'http_cookie_domain': 'test_value_9', + 'http_cookie_domain_from_host': 'disable', + 'http_cookie_generation': '11', + 'http_cookie_path': 'test_value_12', + 'http_cookie_share': 'disable', + 'http_ip_header': 'enable', + 'http_ip_header_name': 'test_value_15', + 'http_multiplex': 'enable', + 'https_cookie_secure': 'disable', + 'id': '18', + 'ldb_method': 'static', + 'mappedip': 'test_value_20', + 'mappedport': 'test_value_21', + 'max_embryonic_connections': '22', + 'name': 'default_name_23', + 'outlook_web_access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'ssl_algorithm': 'high', + 'ssl_certificate': 'test_value_30', + 'ssl_client_fallback': 'disable', + 'ssl_client_renegotiation': 'allow', + 'ssl_client_session_state_max': '33', + 'ssl_client_session_state_timeout': '34', + 'ssl_client_session_state_type': 'disable', + 'ssl_dh_bits': '768', + 'ssl_hpkp': 'disable', + 'ssl_hpkp_age': '38', + 'ssl_hpkp_backup': 'test_value_39', + 'ssl_hpkp_include_subdomains': 'disable', + 'ssl_hpkp_primary': 'test_value_41', + 'ssl_hpkp_report_uri': 'test_value_42', + 'ssl_hsts': 'disable', + 'ssl_hsts_age': '44', + 'ssl_hsts_include_subdomains': 'disable', + 'ssl_http_location_conversion': 'enable', + 'ssl_http_match_host': 'enable', + 'ssl_max_version': 'ssl-3.0', + 'ssl_min_version': 'ssl-3.0', + 'ssl_mode': 'half', + 'ssl_pfs': 'require', + 'ssl_send_empty_frags': 'enable', + 'ssl_server_algorithm': 'high', + 'ssl_server_max_version': 'ssl-3.0', + 'ssl_server_min_version': 'ssl-3.0', + 'ssl_server_session_state_max': '56', + 'ssl_server_session_state_timeout': '57', + 'ssl_server_session_state_type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_60', + 'weblogic_server': 'disable', + 'websphere_server': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip6.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'http-cookie-age': '8', + 'http-cookie-domain': 'test_value_9', + 'http-cookie-domain-from-host': 'disable', + 'http-cookie-generation': '11', + 'http-cookie-path': 'test_value_12', + 'http-cookie-share': 'disable', + 'http-ip-header': 'enable', + 'http-ip-header-name': 'test_value_15', + 'http-multiplex': 'enable', + 'https-cookie-secure': 'disable', + 'id': '18', + 'ldb-method': 'static', + 'mappedip': 'test_value_20', + 'mappedport': 'test_value_21', + 'max-embryonic-connections': '22', + 'name': 'default_name_23', + 'outlook-web-access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server-type': 'http', + 'ssl-algorithm': 'high', + 'ssl-certificate': 'test_value_30', + 'ssl-client-fallback': 'disable', + 'ssl-client-renegotiation': 'allow', + 'ssl-client-session-state-max': '33', + 'ssl-client-session-state-timeout': '34', + 'ssl-client-session-state-type': 'disable', + 'ssl-dh-bits': '768', + 'ssl-hpkp': 'disable', + 'ssl-hpkp-age': '38', + 'ssl-hpkp-backup': 'test_value_39', + 'ssl-hpkp-include-subdomains': 'disable', + 'ssl-hpkp-primary': 'test_value_41', + 'ssl-hpkp-report-uri': 'test_value_42', + 'ssl-hsts': 'disable', + 'ssl-hsts-age': '44', + 'ssl-hsts-include-subdomains': 'disable', + 'ssl-http-location-conversion': 'enable', + 'ssl-http-match-host': 'enable', + 'ssl-max-version': 'ssl-3.0', + 'ssl-min-version': 'ssl-3.0', + 'ssl-mode': 'half', + 'ssl-pfs': 'require', + 'ssl-send-empty-frags': 'enable', + 'ssl-server-algorithm': 'high', + 'ssl-server-max-version': 'ssl-3.0', + 'ssl-server-min-version': 'ssl-3.0', + 'ssl-server-session-state-max': '56', + 'ssl-server-session-state-timeout': '57', + 'ssl-server-session-state-type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_60', + 'weblogic-server': 'disable', + 'websphere-server': 'disable' + } + + set_method_mock.assert_called_with('firewall', 'vip6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vip6_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vip6': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'http_cookie_age': '8', + 'http_cookie_domain': 'test_value_9', + 'http_cookie_domain_from_host': 'disable', + 'http_cookie_generation': '11', + 'http_cookie_path': 'test_value_12', + 'http_cookie_share': 'disable', + 'http_ip_header': 'enable', + 'http_ip_header_name': 'test_value_15', + 'http_multiplex': 'enable', + 'https_cookie_secure': 'disable', + 'id': '18', + 'ldb_method': 'static', + 'mappedip': 'test_value_20', + 'mappedport': 'test_value_21', + 'max_embryonic_connections': '22', + 'name': 'default_name_23', + 'outlook_web_access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'ssl_algorithm': 'high', + 'ssl_certificate': 'test_value_30', + 'ssl_client_fallback': 'disable', + 'ssl_client_renegotiation': 'allow', + 'ssl_client_session_state_max': '33', + 'ssl_client_session_state_timeout': '34', + 'ssl_client_session_state_type': 'disable', + 'ssl_dh_bits': '768', + 'ssl_hpkp': 'disable', + 'ssl_hpkp_age': '38', + 'ssl_hpkp_backup': 'test_value_39', + 'ssl_hpkp_include_subdomains': 'disable', + 'ssl_hpkp_primary': 'test_value_41', + 'ssl_hpkp_report_uri': 'test_value_42', + 'ssl_hsts': 'disable', + 'ssl_hsts_age': '44', + 'ssl_hsts_include_subdomains': 'disable', + 'ssl_http_location_conversion': 'enable', + 'ssl_http_match_host': 'enable', + 'ssl_max_version': 'ssl-3.0', + 'ssl_min_version': 'ssl-3.0', + 'ssl_mode': 'half', + 'ssl_pfs': 'require', + 'ssl_send_empty_frags': 'enable', + 'ssl_server_algorithm': 'high', + 'ssl_server_max_version': 'ssl-3.0', + 'ssl_server_min_version': 'ssl-3.0', + 'ssl_server_session_state_max': '56', + 'ssl_server_session_state_timeout': '57', + 'ssl_server_session_state_type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_60', + 'weblogic_server': 'disable', + 'websphere_server': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip6.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vip6', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vip6_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vip6': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'http_cookie_age': '8', + 'http_cookie_domain': 'test_value_9', + 'http_cookie_domain_from_host': 'disable', + 'http_cookie_generation': '11', + 'http_cookie_path': 'test_value_12', + 'http_cookie_share': 'disable', + 'http_ip_header': 'enable', + 'http_ip_header_name': 'test_value_15', + 'http_multiplex': 'enable', + 'https_cookie_secure': 'disable', + 'id': '18', + 'ldb_method': 'static', + 'mappedip': 'test_value_20', + 'mappedport': 'test_value_21', + 'max_embryonic_connections': '22', + 'name': 'default_name_23', + 'outlook_web_access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'ssl_algorithm': 'high', + 'ssl_certificate': 'test_value_30', + 'ssl_client_fallback': 'disable', + 'ssl_client_renegotiation': 'allow', + 'ssl_client_session_state_max': '33', + 'ssl_client_session_state_timeout': '34', + 'ssl_client_session_state_type': 'disable', + 'ssl_dh_bits': '768', + 'ssl_hpkp': 'disable', + 'ssl_hpkp_age': '38', + 'ssl_hpkp_backup': 'test_value_39', + 'ssl_hpkp_include_subdomains': 'disable', + 'ssl_hpkp_primary': 'test_value_41', + 'ssl_hpkp_report_uri': 'test_value_42', + 'ssl_hsts': 'disable', + 'ssl_hsts_age': '44', + 'ssl_hsts_include_subdomains': 'disable', + 'ssl_http_location_conversion': 'enable', + 'ssl_http_match_host': 'enable', + 'ssl_max_version': 'ssl-3.0', + 'ssl_min_version': 'ssl-3.0', + 'ssl_mode': 'half', + 'ssl_pfs': 'require', + 'ssl_send_empty_frags': 'enable', + 'ssl_server_algorithm': 'high', + 'ssl_server_max_version': 'ssl-3.0', + 'ssl_server_min_version': 'ssl-3.0', + 'ssl_server_session_state_max': '56', + 'ssl_server_session_state_timeout': '57', + 'ssl_server_session_state_type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_60', + 'weblogic_server': 'disable', + 'websphere_server': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip6.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vip6', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vip6_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip6': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'http_cookie_age': '8', + 'http_cookie_domain': 'test_value_9', + 'http_cookie_domain_from_host': 'disable', + 'http_cookie_generation': '11', + 'http_cookie_path': 'test_value_12', + 'http_cookie_share': 'disable', + 'http_ip_header': 'enable', + 'http_ip_header_name': 'test_value_15', + 'http_multiplex': 'enable', + 'https_cookie_secure': 'disable', + 'id': '18', + 'ldb_method': 'static', + 'mappedip': 'test_value_20', + 'mappedport': 'test_value_21', + 'max_embryonic_connections': '22', + 'name': 'default_name_23', + 'outlook_web_access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'ssl_algorithm': 'high', + 'ssl_certificate': 'test_value_30', + 'ssl_client_fallback': 'disable', + 'ssl_client_renegotiation': 'allow', + 'ssl_client_session_state_max': '33', + 'ssl_client_session_state_timeout': '34', + 'ssl_client_session_state_type': 'disable', + 'ssl_dh_bits': '768', + 'ssl_hpkp': 'disable', + 'ssl_hpkp_age': '38', + 'ssl_hpkp_backup': 'test_value_39', + 'ssl_hpkp_include_subdomains': 'disable', + 'ssl_hpkp_primary': 'test_value_41', + 'ssl_hpkp_report_uri': 'test_value_42', + 'ssl_hsts': 'disable', + 'ssl_hsts_age': '44', + 'ssl_hsts_include_subdomains': 'disable', + 'ssl_http_location_conversion': 'enable', + 'ssl_http_match_host': 'enable', + 'ssl_max_version': 'ssl-3.0', + 'ssl_min_version': 'ssl-3.0', + 'ssl_mode': 'half', + 'ssl_pfs': 'require', + 'ssl_send_empty_frags': 'enable', + 'ssl_server_algorithm': 'high', + 'ssl_server_max_version': 'ssl-3.0', + 'ssl_server_min_version': 'ssl-3.0', + 'ssl_server_session_state_max': '56', + 'ssl_server_session_state_timeout': '57', + 'ssl_server_session_state_type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_60', + 'weblogic_server': 'disable', + 'websphere_server': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip6.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'http-cookie-age': '8', + 'http-cookie-domain': 'test_value_9', + 'http-cookie-domain-from-host': 'disable', + 'http-cookie-generation': '11', + 'http-cookie-path': 'test_value_12', + 'http-cookie-share': 'disable', + 'http-ip-header': 'enable', + 'http-ip-header-name': 'test_value_15', + 'http-multiplex': 'enable', + 'https-cookie-secure': 'disable', + 'id': '18', + 'ldb-method': 'static', + 'mappedip': 'test_value_20', + 'mappedport': 'test_value_21', + 'max-embryonic-connections': '22', + 'name': 'default_name_23', + 'outlook-web-access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server-type': 'http', + 'ssl-algorithm': 'high', + 'ssl-certificate': 'test_value_30', + 'ssl-client-fallback': 'disable', + 'ssl-client-renegotiation': 'allow', + 'ssl-client-session-state-max': '33', + 'ssl-client-session-state-timeout': '34', + 'ssl-client-session-state-type': 'disable', + 'ssl-dh-bits': '768', + 'ssl-hpkp': 'disable', + 'ssl-hpkp-age': '38', + 'ssl-hpkp-backup': 'test_value_39', + 'ssl-hpkp-include-subdomains': 'disable', + 'ssl-hpkp-primary': 'test_value_41', + 'ssl-hpkp-report-uri': 'test_value_42', + 'ssl-hsts': 'disable', + 'ssl-hsts-age': '44', + 'ssl-hsts-include-subdomains': 'disable', + 'ssl-http-location-conversion': 'enable', + 'ssl-http-match-host': 'enable', + 'ssl-max-version': 'ssl-3.0', + 'ssl-min-version': 'ssl-3.0', + 'ssl-mode': 'half', + 'ssl-pfs': 'require', + 'ssl-send-empty-frags': 'enable', + 'ssl-server-algorithm': 'high', + 'ssl-server-max-version': 'ssl-3.0', + 'ssl-server-min-version': 'ssl-3.0', + 'ssl-server-session-state-max': '56', + 'ssl-server-session-state-timeout': '57', + 'ssl-server-session-state-type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_60', + 'weblogic-server': 'disable', + 'websphere-server': 'disable' + } + + set_method_mock.assert_called_with('firewall', 'vip6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_firewall_vip6_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip6': { + 'random_attribute_not_valid': 'tag', + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'http_cookie_age': '8', + 'http_cookie_domain': 'test_value_9', + 'http_cookie_domain_from_host': 'disable', + 'http_cookie_generation': '11', + 'http_cookie_path': 'test_value_12', + 'http_cookie_share': 'disable', + 'http_ip_header': 'enable', + 'http_ip_header_name': 'test_value_15', + 'http_multiplex': 'enable', + 'https_cookie_secure': 'disable', + 'id': '18', + 'ldb_method': 'static', + 'mappedip': 'test_value_20', + 'mappedport': 'test_value_21', + 'max_embryonic_connections': '22', + 'name': 'default_name_23', + 'outlook_web_access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'ssl_algorithm': 'high', + 'ssl_certificate': 'test_value_30', + 'ssl_client_fallback': 'disable', + 'ssl_client_renegotiation': 'allow', + 'ssl_client_session_state_max': '33', + 'ssl_client_session_state_timeout': '34', + 'ssl_client_session_state_type': 'disable', + 'ssl_dh_bits': '768', + 'ssl_hpkp': 'disable', + 'ssl_hpkp_age': '38', + 'ssl_hpkp_backup': 'test_value_39', + 'ssl_hpkp_include_subdomains': 'disable', + 'ssl_hpkp_primary': 'test_value_41', + 'ssl_hpkp_report_uri': 'test_value_42', + 'ssl_hsts': 'disable', + 'ssl_hsts_age': '44', + 'ssl_hsts_include_subdomains': 'disable', + 'ssl_http_location_conversion': 'enable', + 'ssl_http_match_host': 'enable', + 'ssl_max_version': 'ssl-3.0', + 'ssl_min_version': 'ssl-3.0', + 'ssl_mode': 'half', + 'ssl_pfs': 'require', + 'ssl_send_empty_frags': 'enable', + 'ssl_server_algorithm': 'high', + 'ssl_server_max_version': 'ssl-3.0', + 'ssl_server_min_version': 'ssl-3.0', + 'ssl_server_session_state_max': '56', + 'ssl_server_session_state_timeout': '57', + 'ssl_server_session_state_type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_60', + 'weblogic_server': 'disable', + 'websphere_server': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip6.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'http-cookie-age': '8', + 'http-cookie-domain': 'test_value_9', + 'http-cookie-domain-from-host': 'disable', + 'http-cookie-generation': '11', + 'http-cookie-path': 'test_value_12', + 'http-cookie-share': 'disable', + 'http-ip-header': 'enable', + 'http-ip-header-name': 'test_value_15', + 'http-multiplex': 'enable', + 'https-cookie-secure': 'disable', + 'id': '18', + 'ldb-method': 'static', + 'mappedip': 'test_value_20', + 'mappedport': 'test_value_21', + 'max-embryonic-connections': '22', + 'name': 'default_name_23', + 'outlook-web-access': 'disable', + 'persistence': 'none', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server-type': 'http', + 'ssl-algorithm': 'high', + 'ssl-certificate': 'test_value_30', + 'ssl-client-fallback': 'disable', + 'ssl-client-renegotiation': 'allow', + 'ssl-client-session-state-max': '33', + 'ssl-client-session-state-timeout': '34', + 'ssl-client-session-state-type': 'disable', + 'ssl-dh-bits': '768', + 'ssl-hpkp': 'disable', + 'ssl-hpkp-age': '38', + 'ssl-hpkp-backup': 'test_value_39', + 'ssl-hpkp-include-subdomains': 'disable', + 'ssl-hpkp-primary': 'test_value_41', + 'ssl-hpkp-report-uri': 'test_value_42', + 'ssl-hsts': 'disable', + 'ssl-hsts-age': '44', + 'ssl-hsts-include-subdomains': 'disable', + 'ssl-http-location-conversion': 'enable', + 'ssl-http-match-host': 'enable', + 'ssl-max-version': 'ssl-3.0', + 'ssl-min-version': 'ssl-3.0', + 'ssl-mode': 'half', + 'ssl-pfs': 'require', + 'ssl-send-empty-frags': 'enable', + 'ssl-server-algorithm': 'high', + 'ssl-server-max-version': 'ssl-3.0', + 'ssl-server-min-version': 'ssl-3.0', + 'ssl-server-session-state-max': '56', + 'ssl-server-session-state-timeout': '57', + 'ssl-server-session-state-type': 'disable', + 'type': 'static-nat', + 'uuid': 'test_value_60', + 'weblogic-server': 'disable', + 'websphere-server': 'disable' + } + + set_method_mock.assert_called_with('firewall', 'vip6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_firewall_vip64.py b/test/units/modules/network/fortios/test_fortios_firewall_vip64.py new file mode 100644 index 00000000000..b496be600b3 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_firewall_vip64.py @@ -0,0 +1,339 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_firewall_vip64 +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_firewall_vip64.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_firewall_vip64_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip64': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb_method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip64.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb-method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server-type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + } + + set_method_mock.assert_called_with('firewall', 'vip64', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vip64_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip64': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb_method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip64.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb-method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server-type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + } + + set_method_mock.assert_called_with('firewall', 'vip64', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vip64_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vip64': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb_method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip64.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vip64', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vip64_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vip64': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb_method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip64.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vip64', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vip64_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip64': { + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb_method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip64.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb-method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server-type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + } + + set_method_mock.assert_called_with('firewall', 'vip64', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_firewall_vip64_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vip64': { + 'random_attribute_not_valid': 'tag', + 'arp_reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb_method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server_type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vip64.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'arp-reply': 'disable', + 'color': '4', + 'comment': 'Comment.', + 'extip': 'test_value_6', + 'extport': 'test_value_7', + 'id': '8', + 'ldb-method': 'static', + 'mappedip': 'test_value_10', + 'mappedport': 'test_value_11', + 'name': 'default_name_12', + 'portforward': 'disable', + 'protocol': 'tcp', + 'server-type': 'http', + 'type': 'static-nat', + 'uuid': 'test_value_17' + } + + set_method_mock.assert_called_with('firewall', 'vip64', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_firewall_vipgrp.py b/test/units/modules/network/fortios/test_fortios_firewall_vipgrp.py new file mode 100644 index 00000000000..b4b94325698 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_firewall_vipgrp.py @@ -0,0 +1,239 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_firewall_vipgrp +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_firewall_vipgrp.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_firewall_vipgrp_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp': { + 'color': '3', + 'comments': 'test_value_4', + 'interface': 'test_value_5', + 'name': 'default_name_6', + 'uuid': 'test_value_7' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'interface': 'test_value_5', + 'name': 'default_name_6', + 'uuid': 'test_value_7' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vipgrp_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp': { + 'color': '3', + 'comments': 'test_value_4', + 'interface': 'test_value_5', + 'name': 'default_name_6', + 'uuid': 'test_value_7' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'interface': 'test_value_5', + 'name': 'default_name_6', + 'uuid': 'test_value_7' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vipgrp_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vipgrp': { + 'color': '3', + 'comments': 'test_value_4', + 'interface': 'test_value_5', + 'name': 'default_name_6', + 'uuid': 'test_value_7' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vipgrp', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vipgrp_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vipgrp': { + 'color': '3', + 'comments': 'test_value_4', + 'interface': 'test_value_5', + 'name': 'default_name_6', + 'uuid': 'test_value_7' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vipgrp', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vipgrp_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp': { + 'color': '3', + 'comments': 'test_value_4', + 'interface': 'test_value_5', + 'name': 'default_name_6', + 'uuid': 'test_value_7' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'interface': 'test_value_5', + 'name': 'default_name_6', + 'uuid': 'test_value_7' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_firewall_vipgrp_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp': { + 'random_attribute_not_valid': 'tag', + 'color': '3', + 'comments': 'test_value_4', + 'interface': 'test_value_5', + 'name': 'default_name_6', + 'uuid': 'test_value_7' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'interface': 'test_value_5', + 'name': 'default_name_6', + 'uuid': 'test_value_7' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_firewall_vipgrp46.py b/test/units/modules/network/fortios/test_fortios_firewall_vipgrp46.py new file mode 100644 index 00000000000..9ab148df7d8 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_firewall_vipgrp46.py @@ -0,0 +1,229 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_firewall_vipgrp46 +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_firewall_vipgrp46.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_firewall_vipgrp46_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp46': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp46.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp46', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vipgrp46_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp46': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp46.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp46', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vipgrp46_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vipgrp46': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp46.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vipgrp46', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vipgrp46_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vipgrp46': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp46.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vipgrp46', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vipgrp46_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp46': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp46.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp46', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_firewall_vipgrp46_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp46': { + 'random_attribute_not_valid': 'tag', + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp46.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp46', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_firewall_vipgrp6.py b/test/units/modules/network/fortios/test_fortios_firewall_vipgrp6.py new file mode 100644 index 00000000000..808a9c26a38 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_firewall_vipgrp6.py @@ -0,0 +1,229 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_firewall_vipgrp6 +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_firewall_vipgrp6.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_firewall_vipgrp6_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp6': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp6.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vipgrp6_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp6': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp6.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vipgrp6_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vipgrp6': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp6.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vipgrp6', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vipgrp6_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vipgrp6': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp6.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vipgrp6', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vipgrp6_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp6': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp6.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_firewall_vipgrp6_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp6': { + 'random_attribute_not_valid': 'tag', + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp6.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_firewall_vipgrp64.py b/test/units/modules/network/fortios/test_fortios_firewall_vipgrp64.py new file mode 100644 index 00000000000..41a48c9fa15 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_firewall_vipgrp64.py @@ -0,0 +1,229 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_firewall_vipgrp64 +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_firewall_vipgrp64.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_firewall_vipgrp64_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp64': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp64.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp64', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vipgrp64_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp64': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp64.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp64', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vipgrp64_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vipgrp64': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp64.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vipgrp64', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_vipgrp64_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_vipgrp64': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp64.fortios_firewall(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall', 'vipgrp64', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_vipgrp64_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp64': { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp64.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp64', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_firewall_vipgrp64_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_vipgrp64': { + 'random_attribute_not_valid': 'tag', + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_vipgrp64.fortios_firewall(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comments': 'test_value_4', + 'name': 'default_name_5', + 'uuid': 'test_value_6' + } + + set_method_mock.assert_called_with('firewall', 'vipgrp64', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_firewall_wildcard_fqdn_custom.py b/test/units/modules/network/fortios/test_fortios_firewall_wildcard_fqdn_custom.py new file mode 100644 index 00000000000..55a91a37470 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_firewall_wildcard_fqdn_custom.py @@ -0,0 +1,249 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_firewall_wildcard_fqdn_custom +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_firewall_wildcard_fqdn_custom.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_firewall_wildcard_fqdn_custom_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_wildcard_fqdn_custom': { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable', + 'wildcard_fqdn': 'test_value_8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_wildcard_fqdn_custom.fortios_firewall_wildcard_fqdn(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable', + 'wildcard-fqdn': 'test_value_8' + } + + set_method_mock.assert_called_with('firewall.wildcard-fqdn', 'custom', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_wildcard_fqdn_custom_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_wildcard_fqdn_custom': { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable', + 'wildcard_fqdn': 'test_value_8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_wildcard_fqdn_custom.fortios_firewall_wildcard_fqdn(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable', + 'wildcard-fqdn': 'test_value_8' + } + + set_method_mock.assert_called_with('firewall.wildcard-fqdn', 'custom', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_wildcard_fqdn_custom_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_wildcard_fqdn_custom': { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable', + 'wildcard_fqdn': 'test_value_8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_wildcard_fqdn_custom.fortios_firewall_wildcard_fqdn(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall.wildcard-fqdn', 'custom', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_wildcard_fqdn_custom_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_wildcard_fqdn_custom': { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable', + 'wildcard_fqdn': 'test_value_8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_wildcard_fqdn_custom.fortios_firewall_wildcard_fqdn(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall.wildcard-fqdn', 'custom', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_wildcard_fqdn_custom_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_wildcard_fqdn_custom': { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable', + 'wildcard_fqdn': 'test_value_8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_wildcard_fqdn_custom.fortios_firewall_wildcard_fqdn(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable', + 'wildcard-fqdn': 'test_value_8' + } + + set_method_mock.assert_called_with('firewall.wildcard-fqdn', 'custom', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_firewall_wildcard_fqdn_custom_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_wildcard_fqdn_custom': { + 'random_attribute_not_valid': 'tag', + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable', + 'wildcard_fqdn': 'test_value_8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_wildcard_fqdn_custom.fortios_firewall_wildcard_fqdn(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable', + 'wildcard-fqdn': 'test_value_8' + } + + set_method_mock.assert_called_with('firewall.wildcard-fqdn', 'custom', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_firewall_wildcard_fqdn_group.py b/test/units/modules/network/fortios/test_fortios_firewall_wildcard_fqdn_group.py new file mode 100644 index 00000000000..59e86978b71 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_firewall_wildcard_fqdn_group.py @@ -0,0 +1,239 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_firewall_wildcard_fqdn_group +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_firewall_wildcard_fqdn_group.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_firewall_wildcard_fqdn_group_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_wildcard_fqdn_group': { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_wildcard_fqdn_group.fortios_firewall_wildcard_fqdn(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable' + } + + set_method_mock.assert_called_with('firewall.wildcard-fqdn', 'group', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_wildcard_fqdn_group_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_wildcard_fqdn_group': { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_wildcard_fqdn_group.fortios_firewall_wildcard_fqdn(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable' + } + + set_method_mock.assert_called_with('firewall.wildcard-fqdn', 'group', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_wildcard_fqdn_group_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_wildcard_fqdn_group': { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_wildcard_fqdn_group.fortios_firewall_wildcard_fqdn(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall.wildcard-fqdn', 'group', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_firewall_wildcard_fqdn_group_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'firewall_wildcard_fqdn_group': { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_wildcard_fqdn_group.fortios_firewall_wildcard_fqdn(input_data, fos_instance) + + delete_method_mock.assert_called_with('firewall.wildcard-fqdn', 'group', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_firewall_wildcard_fqdn_group_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_wildcard_fqdn_group': { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_wildcard_fqdn_group.fortios_firewall_wildcard_fqdn(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable' + } + + set_method_mock.assert_called_with('firewall.wildcard-fqdn', 'group', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_firewall_wildcard_fqdn_group_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'firewall_wildcard_fqdn_group': { + 'random_attribute_not_valid': 'tag', + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_firewall_wildcard_fqdn_group.fortios_firewall_wildcard_fqdn(input_data, fos_instance) + + expected_data = { + 'color': '3', + 'comment': 'Comment.', + 'name': 'default_name_5', + 'uuid': 'test_value_6', + 'visibility': 'enable' + } + + set_method_mock.assert_called_with('firewall.wildcard-fqdn', 'group', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_ftp_proxy_explicit.py b/test/units/modules/network/fortios/test_fortios_ftp_proxy_explicit.py new file mode 100644 index 00000000000..3773eda0e85 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_ftp_proxy_explicit.py @@ -0,0 +1,183 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_ftp_proxy_explicit +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_ftp_proxy_explicit.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_ftp_proxy_explicit_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ftp_proxy_explicit': { + 'incoming_ip': 'test_value_3', + 'incoming_port': 'test_value_4', + 'outgoing_ip': 'test_value_5', + 'sec_default_action': 'accept', + 'status': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ftp_proxy_explicit.fortios_ftp_proxy(input_data, fos_instance) + + expected_data = { + 'incoming-ip': 'test_value_3', + 'incoming-port': 'test_value_4', + 'outgoing-ip': 'test_value_5', + 'sec-default-action': 'accept', + 'status': 'enable' + } + + set_method_mock.assert_called_with('ftp-proxy', 'explicit', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_ftp_proxy_explicit_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ftp_proxy_explicit': { + 'incoming_ip': 'test_value_3', + 'incoming_port': 'test_value_4', + 'outgoing_ip': 'test_value_5', + 'sec_default_action': 'accept', + 'status': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ftp_proxy_explicit.fortios_ftp_proxy(input_data, fos_instance) + + expected_data = { + 'incoming-ip': 'test_value_3', + 'incoming-port': 'test_value_4', + 'outgoing-ip': 'test_value_5', + 'sec-default-action': 'accept', + 'status': 'enable' + } + + set_method_mock.assert_called_with('ftp-proxy', 'explicit', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_ftp_proxy_explicit_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ftp_proxy_explicit': { + 'incoming_ip': 'test_value_3', + 'incoming_port': 'test_value_4', + 'outgoing_ip': 'test_value_5', + 'sec_default_action': 'accept', + 'status': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ftp_proxy_explicit.fortios_ftp_proxy(input_data, fos_instance) + + expected_data = { + 'incoming-ip': 'test_value_3', + 'incoming-port': 'test_value_4', + 'outgoing-ip': 'test_value_5', + 'sec-default-action': 'accept', + 'status': 'enable' + } + + set_method_mock.assert_called_with('ftp-proxy', 'explicit', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_ftp_proxy_explicit_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ftp_proxy_explicit': { + 'random_attribute_not_valid': 'tag', + 'incoming_ip': 'test_value_3', + 'incoming_port': 'test_value_4', + 'outgoing_ip': 'test_value_5', + 'sec_default_action': 'accept', + 'status': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ftp_proxy_explicit.fortios_ftp_proxy(input_data, fos_instance) + + expected_data = { + 'incoming-ip': 'test_value_3', + 'incoming-port': 'test_value_4', + 'outgoing-ip': 'test_value_5', + 'sec-default-action': 'accept', + 'status': 'enable' + } + + set_method_mock.assert_called_with('ftp-proxy', 'explicit', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_icap_profile.py b/test/units/modules/network/fortios/test_fortios_icap_profile.py new file mode 100644 index 00000000000..1e2640bcf1a --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_icap_profile.py @@ -0,0 +1,309 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_icap_profile +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_icap_profile.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_icap_profile_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'icap_profile': { + 'methods': 'delete', + 'name': 'default_name_4', + 'replacemsg_group': 'test_value_5', + 'request': 'disable', + 'request_failure': 'error', + 'request_path': 'test_value_8', + 'request_server': 'test_value_9', + 'response': 'disable', + 'response_failure': 'error', + 'response_path': 'test_value_12', + 'response_server': 'test_value_13', + 'streaming_content_bypass': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_icap_profile.fortios_icap(input_data, fos_instance) + + expected_data = { + 'methods': 'delete', + 'name': 'default_name_4', + 'replacemsg-group': 'test_value_5', + 'request': 'disable', + 'request-failure': 'error', + 'request-path': 'test_value_8', + 'request-server': 'test_value_9', + 'response': 'disable', + 'response-failure': 'error', + 'response-path': 'test_value_12', + 'response-server': 'test_value_13', + 'streaming-content-bypass': 'disable' + } + + set_method_mock.assert_called_with('icap', 'profile', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_icap_profile_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'icap_profile': { + 'methods': 'delete', + 'name': 'default_name_4', + 'replacemsg_group': 'test_value_5', + 'request': 'disable', + 'request_failure': 'error', + 'request_path': 'test_value_8', + 'request_server': 'test_value_9', + 'response': 'disable', + 'response_failure': 'error', + 'response_path': 'test_value_12', + 'response_server': 'test_value_13', + 'streaming_content_bypass': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_icap_profile.fortios_icap(input_data, fos_instance) + + expected_data = { + 'methods': 'delete', + 'name': 'default_name_4', + 'replacemsg-group': 'test_value_5', + 'request': 'disable', + 'request-failure': 'error', + 'request-path': 'test_value_8', + 'request-server': 'test_value_9', + 'response': 'disable', + 'response-failure': 'error', + 'response-path': 'test_value_12', + 'response-server': 'test_value_13', + 'streaming-content-bypass': 'disable' + } + + set_method_mock.assert_called_with('icap', 'profile', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_icap_profile_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'icap_profile': { + 'methods': 'delete', + 'name': 'default_name_4', + 'replacemsg_group': 'test_value_5', + 'request': 'disable', + 'request_failure': 'error', + 'request_path': 'test_value_8', + 'request_server': 'test_value_9', + 'response': 'disable', + 'response_failure': 'error', + 'response_path': 'test_value_12', + 'response_server': 'test_value_13', + 'streaming_content_bypass': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_icap_profile.fortios_icap(input_data, fos_instance) + + delete_method_mock.assert_called_with('icap', 'profile', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_icap_profile_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'icap_profile': { + 'methods': 'delete', + 'name': 'default_name_4', + 'replacemsg_group': 'test_value_5', + 'request': 'disable', + 'request_failure': 'error', + 'request_path': 'test_value_8', + 'request_server': 'test_value_9', + 'response': 'disable', + 'response_failure': 'error', + 'response_path': 'test_value_12', + 'response_server': 'test_value_13', + 'streaming_content_bypass': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_icap_profile.fortios_icap(input_data, fos_instance) + + delete_method_mock.assert_called_with('icap', 'profile', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_icap_profile_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'icap_profile': { + 'methods': 'delete', + 'name': 'default_name_4', + 'replacemsg_group': 'test_value_5', + 'request': 'disable', + 'request_failure': 'error', + 'request_path': 'test_value_8', + 'request_server': 'test_value_9', + 'response': 'disable', + 'response_failure': 'error', + 'response_path': 'test_value_12', + 'response_server': 'test_value_13', + 'streaming_content_bypass': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_icap_profile.fortios_icap(input_data, fos_instance) + + expected_data = { + 'methods': 'delete', + 'name': 'default_name_4', + 'replacemsg-group': 'test_value_5', + 'request': 'disable', + 'request-failure': 'error', + 'request-path': 'test_value_8', + 'request-server': 'test_value_9', + 'response': 'disable', + 'response-failure': 'error', + 'response-path': 'test_value_12', + 'response-server': 'test_value_13', + 'streaming-content-bypass': 'disable' + } + + set_method_mock.assert_called_with('icap', 'profile', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_icap_profile_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'icap_profile': { + 'random_attribute_not_valid': 'tag', + 'methods': 'delete', + 'name': 'default_name_4', + 'replacemsg_group': 'test_value_5', + 'request': 'disable', + 'request_failure': 'error', + 'request_path': 'test_value_8', + 'request_server': 'test_value_9', + 'response': 'disable', + 'response_failure': 'error', + 'response_path': 'test_value_12', + 'response_server': 'test_value_13', + 'streaming_content_bypass': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_icap_profile.fortios_icap(input_data, fos_instance) + + expected_data = { + 'methods': 'delete', + 'name': 'default_name_4', + 'replacemsg-group': 'test_value_5', + 'request': 'disable', + 'request-failure': 'error', + 'request-path': 'test_value_8', + 'request-server': 'test_value_9', + 'response': 'disable', + 'response-failure': 'error', + 'response-path': 'test_value_12', + 'response-server': 'test_value_13', + 'streaming-content-bypass': 'disable' + } + + set_method_mock.assert_called_with('icap', 'profile', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_icap_server.py b/test/units/modules/network/fortios/test_fortios_icap_server.py new file mode 100644 index 00000000000..f430c3a219e --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_icap_server.py @@ -0,0 +1,249 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_icap_server +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_icap_server.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_icap_server_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'icap_server': { + 'ip_address': 'test_value_3', + 'ip_version': '4', + 'ip6_address': 'test_value_5', + 'max_connections': '6', + 'name': 'default_name_7', + 'port': '8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_icap_server.fortios_icap(input_data, fos_instance) + + expected_data = { + 'ip-address': 'test_value_3', + 'ip-version': '4', + 'ip6-address': 'test_value_5', + 'max-connections': '6', + 'name': 'default_name_7', + 'port': '8' + } + + set_method_mock.assert_called_with('icap', 'server', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_icap_server_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'icap_server': { + 'ip_address': 'test_value_3', + 'ip_version': '4', + 'ip6_address': 'test_value_5', + 'max_connections': '6', + 'name': 'default_name_7', + 'port': '8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_icap_server.fortios_icap(input_data, fos_instance) + + expected_data = { + 'ip-address': 'test_value_3', + 'ip-version': '4', + 'ip6-address': 'test_value_5', + 'max-connections': '6', + 'name': 'default_name_7', + 'port': '8' + } + + set_method_mock.assert_called_with('icap', 'server', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_icap_server_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'icap_server': { + 'ip_address': 'test_value_3', + 'ip_version': '4', + 'ip6_address': 'test_value_5', + 'max_connections': '6', + 'name': 'default_name_7', + 'port': '8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_icap_server.fortios_icap(input_data, fos_instance) + + delete_method_mock.assert_called_with('icap', 'server', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_icap_server_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'icap_server': { + 'ip_address': 'test_value_3', + 'ip_version': '4', + 'ip6_address': 'test_value_5', + 'max_connections': '6', + 'name': 'default_name_7', + 'port': '8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_icap_server.fortios_icap(input_data, fos_instance) + + delete_method_mock.assert_called_with('icap', 'server', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_icap_server_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'icap_server': { + 'ip_address': 'test_value_3', + 'ip_version': '4', + 'ip6_address': 'test_value_5', + 'max_connections': '6', + 'name': 'default_name_7', + 'port': '8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_icap_server.fortios_icap(input_data, fos_instance) + + expected_data = { + 'ip-address': 'test_value_3', + 'ip-version': '4', + 'ip6-address': 'test_value_5', + 'max-connections': '6', + 'name': 'default_name_7', + 'port': '8' + } + + set_method_mock.assert_called_with('icap', 'server', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_icap_server_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'icap_server': { + 'random_attribute_not_valid': 'tag', + 'ip_address': 'test_value_3', + 'ip_version': '4', + 'ip6_address': 'test_value_5', + 'max_connections': '6', + 'name': 'default_name_7', + 'port': '8' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_icap_server.fortios_icap(input_data, fos_instance) + + expected_data = { + 'ip-address': 'test_value_3', + 'ip-version': '4', + 'ip6-address': 'test_value_5', + 'max-connections': '6', + 'name': 'default_name_7', + 'port': '8' + } + + set_method_mock.assert_called_with('icap', 'server', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_ips_custom.py b/test/units/modules/network/fortios/test_fortios_ips_custom.py new file mode 100644 index 00000000000..17b0599ec18 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_ips_custom.py @@ -0,0 +1,329 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_ips_custom +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_ips_custom.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_ips_custom_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_custom': { + 'action': 'pass', + 'application': 'test_value_4', + 'comment': 'Comment.', + 'location': 'test_value_6', + 'log': 'disable', + 'log_packet': 'disable', + 'os': 'test_value_9', + 'protocol': 'test_value_10', + 'rule_id': '11', + 'severity': 'test_value_12', + 'sig_name': 'test_value_13', + 'signature': 'test_value_14', + 'status': 'disable', + 'tag': 'test_value_16' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_custom.fortios_ips(input_data, fos_instance) + + expected_data = { + 'action': 'pass', + 'application': 'test_value_4', + 'comment': 'Comment.', + 'location': 'test_value_6', + 'log': 'disable', + 'log-packet': 'disable', + 'os': 'test_value_9', + 'protocol': 'test_value_10', + 'rule-id': '11', + 'severity': 'test_value_12', + 'sig-name': 'test_value_13', + 'signature': 'test_value_14', + 'status': 'disable', + 'tag': 'test_value_16' + } + + set_method_mock.assert_called_with('ips', 'custom', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_ips_custom_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_custom': { + 'action': 'pass', + 'application': 'test_value_4', + 'comment': 'Comment.', + 'location': 'test_value_6', + 'log': 'disable', + 'log_packet': 'disable', + 'os': 'test_value_9', + 'protocol': 'test_value_10', + 'rule_id': '11', + 'severity': 'test_value_12', + 'sig_name': 'test_value_13', + 'signature': 'test_value_14', + 'status': 'disable', + 'tag': 'test_value_16' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_custom.fortios_ips(input_data, fos_instance) + + expected_data = { + 'action': 'pass', + 'application': 'test_value_4', + 'comment': 'Comment.', + 'location': 'test_value_6', + 'log': 'disable', + 'log-packet': 'disable', + 'os': 'test_value_9', + 'protocol': 'test_value_10', + 'rule-id': '11', + 'severity': 'test_value_12', + 'sig-name': 'test_value_13', + 'signature': 'test_value_14', + 'status': 'disable', + 'tag': 'test_value_16' + } + + set_method_mock.assert_called_with('ips', 'custom', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_ips_custom_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'ips_custom': { + 'action': 'pass', + 'application': 'test_value_4', + 'comment': 'Comment.', + 'location': 'test_value_6', + 'log': 'disable', + 'log_packet': 'disable', + 'os': 'test_value_9', + 'protocol': 'test_value_10', + 'rule_id': '11', + 'severity': 'test_value_12', + 'sig_name': 'test_value_13', + 'signature': 'test_value_14', + 'status': 'disable', + 'tag': 'test_value_16' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_custom.fortios_ips(input_data, fos_instance) + + delete_method_mock.assert_called_with('ips', 'custom', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_ips_custom_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'ips_custom': { + 'action': 'pass', + 'application': 'test_value_4', + 'comment': 'Comment.', + 'location': 'test_value_6', + 'log': 'disable', + 'log_packet': 'disable', + 'os': 'test_value_9', + 'protocol': 'test_value_10', + 'rule_id': '11', + 'severity': 'test_value_12', + 'sig_name': 'test_value_13', + 'signature': 'test_value_14', + 'status': 'disable', + 'tag': 'test_value_16' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_custom.fortios_ips(input_data, fos_instance) + + delete_method_mock.assert_called_with('ips', 'custom', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_ips_custom_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_custom': { + 'action': 'pass', + 'application': 'test_value_4', + 'comment': 'Comment.', + 'location': 'test_value_6', + 'log': 'disable', + 'log_packet': 'disable', + 'os': 'test_value_9', + 'protocol': 'test_value_10', + 'rule_id': '11', + 'severity': 'test_value_12', + 'sig_name': 'test_value_13', + 'signature': 'test_value_14', + 'status': 'disable', + 'tag': 'test_value_16' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_custom.fortios_ips(input_data, fos_instance) + + expected_data = { + 'action': 'pass', + 'application': 'test_value_4', + 'comment': 'Comment.', + 'location': 'test_value_6', + 'log': 'disable', + 'log-packet': 'disable', + 'os': 'test_value_9', + 'protocol': 'test_value_10', + 'rule-id': '11', + 'severity': 'test_value_12', + 'sig-name': 'test_value_13', + 'signature': 'test_value_14', + 'status': 'disable', + 'tag': 'test_value_16' + } + + set_method_mock.assert_called_with('ips', 'custom', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_ips_custom_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_custom': { + 'random_attribute_not_valid': 'tag', + 'action': 'pass', + 'application': 'test_value_4', + 'comment': 'Comment.', + 'location': 'test_value_6', + 'log': 'disable', + 'log_packet': 'disable', + 'os': 'test_value_9', + 'protocol': 'test_value_10', + 'rule_id': '11', + 'severity': 'test_value_12', + 'sig_name': 'test_value_13', + 'signature': 'test_value_14', + 'status': 'disable', + 'tag': 'test_value_16' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_custom.fortios_ips(input_data, fos_instance) + + expected_data = { + 'action': 'pass', + 'application': 'test_value_4', + 'comment': 'Comment.', + 'location': 'test_value_6', + 'log': 'disable', + 'log-packet': 'disable', + 'os': 'test_value_9', + 'protocol': 'test_value_10', + 'rule-id': '11', + 'severity': 'test_value_12', + 'sig-name': 'test_value_13', + 'signature': 'test_value_14', + 'status': 'disable', + 'tag': 'test_value_16' + } + + set_method_mock.assert_called_with('ips', 'custom', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_ips_decoder.py b/test/units/modules/network/fortios/test_fortios_ips_decoder.py new file mode 100644 index 00000000000..4b1ed124166 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_ips_decoder.py @@ -0,0 +1,209 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_ips_decoder +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_ips_decoder.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_ips_decoder_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_decoder': { + 'name': 'default_name_3', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_decoder.fortios_ips(input_data, fos_instance) + + expected_data = { + 'name': 'default_name_3', + + } + + set_method_mock.assert_called_with('ips', 'decoder', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_ips_decoder_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_decoder': { + 'name': 'default_name_3', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_decoder.fortios_ips(input_data, fos_instance) + + expected_data = { + 'name': 'default_name_3', + + } + + set_method_mock.assert_called_with('ips', 'decoder', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_ips_decoder_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'ips_decoder': { + 'name': 'default_name_3', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_decoder.fortios_ips(input_data, fos_instance) + + delete_method_mock.assert_called_with('ips', 'decoder', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_ips_decoder_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'ips_decoder': { + 'name': 'default_name_3', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_decoder.fortios_ips(input_data, fos_instance) + + delete_method_mock.assert_called_with('ips', 'decoder', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_ips_decoder_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_decoder': { + 'name': 'default_name_3', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_decoder.fortios_ips(input_data, fos_instance) + + expected_data = { + 'name': 'default_name_3', + + } + + set_method_mock.assert_called_with('ips', 'decoder', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_ips_decoder_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_decoder': { + 'random_attribute_not_valid': 'tag', + 'name': 'default_name_3', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_decoder.fortios_ips(input_data, fos_instance) + + expected_data = { + 'name': 'default_name_3', + + } + + set_method_mock.assert_called_with('ips', 'decoder', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_ips_global.py b/test/units/modules/network/fortios/test_fortios_ips_global.py new file mode 100644 index 00000000000..a0772ab7ecd --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_ips_global.py @@ -0,0 +1,247 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_ips_global +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_ips_global.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_ips_global_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_global': { + 'anomaly_mode': 'periodical', + 'database': 'regular', + 'deep_app_insp_db_limit': '5', + 'deep_app_insp_timeout': '6', + 'engine_count': '7', + 'exclude_signatures': 'none', + 'fail_open': 'enable', + 'intelligent_mode': 'enable', + 'session_limit_mode': 'accurate', + 'skype_client_public_ipaddr': 'test_value_12', + 'socket_size': '13', + 'sync_session_ttl': 'enable', + 'traffic_submit': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_global.fortios_ips(input_data, fos_instance) + + expected_data = { + 'anomaly-mode': 'periodical', + 'database': 'regular', + 'deep-app-insp-db-limit': '5', + 'deep-app-insp-timeout': '6', + 'engine-count': '7', + 'exclude-signatures': 'none', + 'fail-open': 'enable', + 'intelligent-mode': 'enable', + 'session-limit-mode': 'accurate', + 'skype-client-public-ipaddr': 'test_value_12', + 'socket-size': '13', + 'sync-session-ttl': 'enable', + 'traffic-submit': 'enable' + } + + set_method_mock.assert_called_with('ips', 'global', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_ips_global_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_global': { + 'anomaly_mode': 'periodical', + 'database': 'regular', + 'deep_app_insp_db_limit': '5', + 'deep_app_insp_timeout': '6', + 'engine_count': '7', + 'exclude_signatures': 'none', + 'fail_open': 'enable', + 'intelligent_mode': 'enable', + 'session_limit_mode': 'accurate', + 'skype_client_public_ipaddr': 'test_value_12', + 'socket_size': '13', + 'sync_session_ttl': 'enable', + 'traffic_submit': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_global.fortios_ips(input_data, fos_instance) + + expected_data = { + 'anomaly-mode': 'periodical', + 'database': 'regular', + 'deep-app-insp-db-limit': '5', + 'deep-app-insp-timeout': '6', + 'engine-count': '7', + 'exclude-signatures': 'none', + 'fail-open': 'enable', + 'intelligent-mode': 'enable', + 'session-limit-mode': 'accurate', + 'skype-client-public-ipaddr': 'test_value_12', + 'socket-size': '13', + 'sync-session-ttl': 'enable', + 'traffic-submit': 'enable' + } + + set_method_mock.assert_called_with('ips', 'global', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_ips_global_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_global': { + 'anomaly_mode': 'periodical', + 'database': 'regular', + 'deep_app_insp_db_limit': '5', + 'deep_app_insp_timeout': '6', + 'engine_count': '7', + 'exclude_signatures': 'none', + 'fail_open': 'enable', + 'intelligent_mode': 'enable', + 'session_limit_mode': 'accurate', + 'skype_client_public_ipaddr': 'test_value_12', + 'socket_size': '13', + 'sync_session_ttl': 'enable', + 'traffic_submit': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_global.fortios_ips(input_data, fos_instance) + + expected_data = { + 'anomaly-mode': 'periodical', + 'database': 'regular', + 'deep-app-insp-db-limit': '5', + 'deep-app-insp-timeout': '6', + 'engine-count': '7', + 'exclude-signatures': 'none', + 'fail-open': 'enable', + 'intelligent-mode': 'enable', + 'session-limit-mode': 'accurate', + 'skype-client-public-ipaddr': 'test_value_12', + 'socket-size': '13', + 'sync-session-ttl': 'enable', + 'traffic-submit': 'enable' + } + + set_method_mock.assert_called_with('ips', 'global', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_ips_global_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_global': { + 'random_attribute_not_valid': 'tag', + 'anomaly_mode': 'periodical', + 'database': 'regular', + 'deep_app_insp_db_limit': '5', + 'deep_app_insp_timeout': '6', + 'engine_count': '7', + 'exclude_signatures': 'none', + 'fail_open': 'enable', + 'intelligent_mode': 'enable', + 'session_limit_mode': 'accurate', + 'skype_client_public_ipaddr': 'test_value_12', + 'socket_size': '13', + 'sync_session_ttl': 'enable', + 'traffic_submit': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_global.fortios_ips(input_data, fos_instance) + + expected_data = { + 'anomaly-mode': 'periodical', + 'database': 'regular', + 'deep-app-insp-db-limit': '5', + 'deep-app-insp-timeout': '6', + 'engine-count': '7', + 'exclude-signatures': 'none', + 'fail-open': 'enable', + 'intelligent-mode': 'enable', + 'session-limit-mode': 'accurate', + 'skype-client-public-ipaddr': 'test_value_12', + 'socket-size': '13', + 'sync-session-ttl': 'enable', + 'traffic-submit': 'enable' + } + + set_method_mock.assert_called_with('ips', 'global', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_ips_rule.py b/test/units/modules/network/fortios/test_fortios_ips_rule.py new file mode 100644 index 00000000000..5ca2e06e26a --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_ips_rule.py @@ -0,0 +1,329 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_ips_rule +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_ips_rule.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_ips_rule_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_rule': { + 'action': 'pass', + 'application': 'test_value_4', + 'date': '5', + 'group': 'test_value_6', + 'location': 'test_value_7,', + 'log': 'disable', + 'log_packet': 'disable', + 'name': 'default_name_10', + 'os': 'test_value_11', + 'rev': '12', + 'rule_id': '13', + 'service': 'test_value_14', + 'severity': 'test_value_15,', + 'status': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_rule.fortios_ips(input_data, fos_instance) + + expected_data = { + 'action': 'pass', + 'application': 'test_value_4', + 'date': '5', + 'group': 'test_value_6', + 'location': 'test_value_7,', + 'log': 'disable', + 'log-packet': 'disable', + 'name': 'default_name_10', + 'os': 'test_value_11', + 'rev': '12', + 'rule-id': '13', + 'service': 'test_value_14', + 'severity': 'test_value_15,', + 'status': 'disable' + } + + set_method_mock.assert_called_with('ips', 'rule', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_ips_rule_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_rule': { + 'action': 'pass', + 'application': 'test_value_4', + 'date': '5', + 'group': 'test_value_6', + 'location': 'test_value_7,', + 'log': 'disable', + 'log_packet': 'disable', + 'name': 'default_name_10', + 'os': 'test_value_11', + 'rev': '12', + 'rule_id': '13', + 'service': 'test_value_14', + 'severity': 'test_value_15,', + 'status': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_rule.fortios_ips(input_data, fos_instance) + + expected_data = { + 'action': 'pass', + 'application': 'test_value_4', + 'date': '5', + 'group': 'test_value_6', + 'location': 'test_value_7,', + 'log': 'disable', + 'log-packet': 'disable', + 'name': 'default_name_10', + 'os': 'test_value_11', + 'rev': '12', + 'rule-id': '13', + 'service': 'test_value_14', + 'severity': 'test_value_15,', + 'status': 'disable' + } + + set_method_mock.assert_called_with('ips', 'rule', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_ips_rule_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'ips_rule': { + 'action': 'pass', + 'application': 'test_value_4', + 'date': '5', + 'group': 'test_value_6', + 'location': 'test_value_7,', + 'log': 'disable', + 'log_packet': 'disable', + 'name': 'default_name_10', + 'os': 'test_value_11', + 'rev': '12', + 'rule_id': '13', + 'service': 'test_value_14', + 'severity': 'test_value_15,', + 'status': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_rule.fortios_ips(input_data, fos_instance) + + delete_method_mock.assert_called_with('ips', 'rule', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_ips_rule_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'ips_rule': { + 'action': 'pass', + 'application': 'test_value_4', + 'date': '5', + 'group': 'test_value_6', + 'location': 'test_value_7,', + 'log': 'disable', + 'log_packet': 'disable', + 'name': 'default_name_10', + 'os': 'test_value_11', + 'rev': '12', + 'rule_id': '13', + 'service': 'test_value_14', + 'severity': 'test_value_15,', + 'status': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_rule.fortios_ips(input_data, fos_instance) + + delete_method_mock.assert_called_with('ips', 'rule', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_ips_rule_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_rule': { + 'action': 'pass', + 'application': 'test_value_4', + 'date': '5', + 'group': 'test_value_6', + 'location': 'test_value_7,', + 'log': 'disable', + 'log_packet': 'disable', + 'name': 'default_name_10', + 'os': 'test_value_11', + 'rev': '12', + 'rule_id': '13', + 'service': 'test_value_14', + 'severity': 'test_value_15,', + 'status': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_rule.fortios_ips(input_data, fos_instance) + + expected_data = { + 'action': 'pass', + 'application': 'test_value_4', + 'date': '5', + 'group': 'test_value_6', + 'location': 'test_value_7,', + 'log': 'disable', + 'log-packet': 'disable', + 'name': 'default_name_10', + 'os': 'test_value_11', + 'rev': '12', + 'rule-id': '13', + 'service': 'test_value_14', + 'severity': 'test_value_15,', + 'status': 'disable' + } + + set_method_mock.assert_called_with('ips', 'rule', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_ips_rule_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_rule': { + 'random_attribute_not_valid': 'tag', + 'action': 'pass', + 'application': 'test_value_4', + 'date': '5', + 'group': 'test_value_6', + 'location': 'test_value_7,', + 'log': 'disable', + 'log_packet': 'disable', + 'name': 'default_name_10', + 'os': 'test_value_11', + 'rev': '12', + 'rule_id': '13', + 'service': 'test_value_14', + 'severity': 'test_value_15,', + 'status': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_rule.fortios_ips(input_data, fos_instance) + + expected_data = { + 'action': 'pass', + 'application': 'test_value_4', + 'date': '5', + 'group': 'test_value_6', + 'location': 'test_value_7,', + 'log': 'disable', + 'log-packet': 'disable', + 'name': 'default_name_10', + 'os': 'test_value_11', + 'rev': '12', + 'rule-id': '13', + 'service': 'test_value_14', + 'severity': 'test_value_15,', + 'status': 'disable' + } + + set_method_mock.assert_called_with('ips', 'rule', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_ips_rule_settings.py b/test/units/modules/network/fortios/test_fortios_ips_rule_settings.py new file mode 100644 index 00000000000..73dc9015fb8 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_ips_rule_settings.py @@ -0,0 +1,199 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_ips_rule_settings +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_ips_rule_settings.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_ips_rule_settings_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_rule_settings': { + 'id': '3' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_rule_settings.fortios_ips(input_data, fos_instance) + + expected_data = { + 'id': '3' + } + + set_method_mock.assert_called_with('ips', 'rule-settings', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_ips_rule_settings_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_rule_settings': { + 'id': '3' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_rule_settings.fortios_ips(input_data, fos_instance) + + expected_data = { + 'id': '3' + } + + set_method_mock.assert_called_with('ips', 'rule-settings', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_ips_rule_settings_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'ips_rule_settings': { + 'id': '3' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_rule_settings.fortios_ips(input_data, fos_instance) + + delete_method_mock.assert_called_with('ips', 'rule-settings', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_ips_rule_settings_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'ips_rule_settings': { + 'id': '3' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_rule_settings.fortios_ips(input_data, fos_instance) + + delete_method_mock.assert_called_with('ips', 'rule-settings', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_ips_rule_settings_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_rule_settings': { + 'id': '3' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_rule_settings.fortios_ips(input_data, fos_instance) + + expected_data = { + 'id': '3' + } + + set_method_mock.assert_called_with('ips', 'rule-settings', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_ips_rule_settings_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'ips_rule_settings': { + 'random_attribute_not_valid': 'tag', + 'id': '3' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_ips_rule_settings.fortios_ips(input_data, fos_instance) + + expected_data = { + 'id': '3' + } + + set_method_mock.assert_called_with('ips', 'rule-settings', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200