From da762018e874bd4abcac1d060d08ae2be4e31126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Angel=20Mu=C3=B1oz=20Gonz=C3=A1lez?= Date: Wed, 28 Aug 2019 09:55:00 +0200 Subject: [PATCH] FortiOS modules for 2.9 - 11 (#61387) --- .../fortios/fortios_web_proxy_explicit.py | 322 +++-- .../fortios/fortios_web_proxy_global.py | 224 +-- .../fortios/fortios_web_proxy_profile.py | 200 ++- .../fortios/fortios_webfilter_fortiguard.py | 222 +-- .../fortios_webfilter_ftgd_local_cat.py | 160 ++- .../fortios_webfilter_ftgd_local_rating.py | 160 ++- ...s_webfilter_ips_urlfilter_cache_setting.py | 145 +- ...fortios_webfilter_ips_urlfilter_setting.py | 141 +- ...ortios_webfilter_ips_urlfilter_setting6.py | 141 +- .../fortios/fortios_webfilter_override.py | 189 ++- .../fortios/fortios_webfilter_profile.py | 600 ++++---- .../fortios_webfilter_search_engine.py | 178 ++- .../fortios/fortios_webfilter_urlfilter.py | 197 ++- .../fortios_wireless_controller_global.py | 236 ++-- .../fortios_wireless_controller_setting.py | 132 +- ...fortios_wireless_controller_utm_profile.py | 186 ++- .../fortios_wireless_controller_vap.py | 913 +++++++----- ...ortios_wireless_controller_wids_profile.py | 551 +++++--- .../fortios_wireless_controller_wtp.py | 707 ++++++---- ...fortios_wireless_controller_wtp_profile.py | 1240 ++++++++++------- test/sanity/ignore.txt | 38 - .../test_fortios_web_proxy_explicit.py | 351 +++++ .../fortios/test_fortios_web_proxy_global.py | 247 ++++ .../fortios/test_fortios_web_proxy_profile.py | 289 ++++ .../test_fortios_webfilter_fortiguard.py | 231 +++ .../test_fortios_webfilter_ftgd_local_cat.py | 219 +++ ...est_fortios_webfilter_ftgd_local_rating.py | 219 +++ ...s_webfilter_ips_urlfilter_cache_setting.py | 159 +++ ...fortios_webfilter_ips_urlfilter_setting.py | 175 +++ ...ortios_webfilter_ips_urlfilter_setting6.py | 175 +++ .../test_fortios_webfilter_override.py | 299 ++++ .../fortios/test_fortios_webfilter_profile.py | 479 +++++++ .../test_fortios_webfilter_search_engine.py | 259 ++++ .../test_fortios_webfilter_urlfilter.py | 239 ++++ ...test_fortios_wireless_controller_global.py | 279 ++++ ...est_fortios_wireless_controller_setting.py | 175 +++ ...fortios_wireless_controller_utm_profile.py | 269 ++++ .../test_fortios_wireless_controller_vap.py | 1109 +++++++++++++++ ...ortios_wireless_controller_wids_profile.py | 679 +++++++++ .../test_fortios_wireless_controller_wtp.py | 509 +++++++ ...fortios_wireless_controller_wtp_profile.py | 439 ++++++ 41 files changed, 11006 insertions(+), 2676 deletions(-) create mode 100644 test/units/modules/network/fortios/test_fortios_web_proxy_explicit.py create mode 100644 test/units/modules/network/fortios/test_fortios_web_proxy_global.py create mode 100644 test/units/modules/network/fortios/test_fortios_web_proxy_profile.py create mode 100644 test/units/modules/network/fortios/test_fortios_webfilter_fortiguard.py create mode 100644 test/units/modules/network/fortios/test_fortios_webfilter_ftgd_local_cat.py create mode 100644 test/units/modules/network/fortios/test_fortios_webfilter_ftgd_local_rating.py create mode 100644 test/units/modules/network/fortios/test_fortios_webfilter_ips_urlfilter_cache_setting.py create mode 100644 test/units/modules/network/fortios/test_fortios_webfilter_ips_urlfilter_setting.py create mode 100644 test/units/modules/network/fortios/test_fortios_webfilter_ips_urlfilter_setting6.py create mode 100644 test/units/modules/network/fortios/test_fortios_webfilter_override.py create mode 100644 test/units/modules/network/fortios/test_fortios_webfilter_profile.py create mode 100644 test/units/modules/network/fortios/test_fortios_webfilter_search_engine.py create mode 100644 test/units/modules/network/fortios/test_fortios_webfilter_urlfilter.py create mode 100644 test/units/modules/network/fortios/test_fortios_wireless_controller_global.py create mode 100644 test/units/modules/network/fortios/test_fortios_wireless_controller_setting.py create mode 100644 test/units/modules/network/fortios/test_fortios_wireless_controller_utm_profile.py create mode 100644 test/units/modules/network/fortios/test_fortios_wireless_controller_vap.py create mode 100644 test/units/modules/network/fortios/test_fortios_wireless_controller_wids_profile.py create mode 100644 test/units/modules/network/fortios/test_fortios_wireless_controller_wtp.py create mode 100644 test/units/modules/network/fortios/test_fortios_wireless_controller_wtp_profile.py diff --git a/lib/ansible/modules/network/fortios/fortios_web_proxy_explicit.py b/lib/ansible/modules/network/fortios/fortios_web_proxy_explicit.py index af1033c3054..fa545cb9809 100644 --- a/lib/ansible/modules/network/fortios/fortios_web_proxy_explicit.py +++ b/lib/ansible/modules/network/fortios/fortios_web_proxy_explicit.py @@ -26,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_web_proxy_explicit short_description: Configure explicit Web proxy settings in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify web_proxy feature and explicit category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -41,195 +41,242 @@ 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: true + version_added: 2.9 web_proxy_explicit: description: - Configure explicit Web proxy settings. default: null + type: dict suboptions: - ftp-incoming-port: + ftp_incoming_port: description: - - Accept incoming FTP-over-HTTP requests on one or more ports (0 - 65535, default = 0; use the same as HTTP). - ftp-over-http: + - Accept incoming FTP-over-HTTP requests on one or more ports (0 - 65535). + type: str + ftp_over_http: description: - Enable to proxy FTP-over-HTTP sessions sent from a web browser. + type: str choices: - enable - disable - http-incoming-port: + http_incoming_port: description: - - Accept incoming HTTP requests on one or more ports (0 - 65535, default = 8080). - https-incoming-port: + - Accept incoming HTTP requests on one or more ports (0 - 65535). + type: str + https_incoming_port: description: - - Accept incoming HTTPS requests on one or more ports (0 - 65535, default = 0, use the same as HTTP). - https-replacement-message: + - Accept incoming HTTPS requests on one or more ports (0 - 65535). + type: str + https_replacement_message: description: - Enable/disable sending the client a replacement message for HTTPS requests. + type: str choices: - enable - disable - incoming-ip: + incoming_ip: description: - Restrict the explicit HTTP proxy to only accept sessions from this IP address. An interface must have this IP address. - incoming-ip6: + type: str + incoming_ip6: description: - Restrict the explicit web proxy to only accept sessions from this IPv6 address. An interface must have this IPv6 address. - ipv6-status: + type: str + ipv6_status: description: - Enable/disable allowing an IPv6 web proxy destination in policies and all IPv6 related entries in this command. + type: str choices: - enable - disable - message-upon-server-error: + message_upon_server_error: description: - Enable/disable displaying a replacement message when a server error is detected. + type: str choices: - enable - disable - outgoing-ip: + outgoing_ip: description: - Outgoing HTTP requests will have this IP address as their source address. An interface must have this IP address. - outgoing-ip6: + type: str + outgoing_ip6: description: - Outgoing HTTP requests will leave this IPv6. Multiple interfaces can be specified. Interfaces must have these IPv6 addresses. - pac-file-data: + type: str + pac_file_data: description: - PAC file contents enclosed in quotes (maximum of 256K bytes). - pac-file-name: + type: str + pac_file_name: description: - Pac file name. - pac-file-server-port: + type: str + pac_file_server_port: description: - - Port number that PAC traffic from client web browsers uses to connect to the explicit web proxy (0 - 65535, default = 0; use the same as - HTTP). - pac-file-server-status: + - Port number that PAC traffic from client web browsers uses to connect to the explicit web proxy (0 - 65535). + type: str + pac_file_server_status: description: - Enable/disable Proxy Auto-Configuration (PAC) for users of this explicit proxy profile. + type: str choices: - enable - disable - pac-file-url: + pac_file_url: description: - PAC file access URL. - pac-policy: + type: str + pac_policy: description: - PAC policies. + type: list suboptions: comments: description: - Optional comments. + type: str dstaddr: description: - Destination address objects. + type: list suboptions: name: description: - Address name. Source firewall.address.name firewall.addrgrp.name. required: true - pac-file-data: + type: str + pac_file_data: description: - PAC file contents enclosed in quotes (maximum of 256K bytes). - pac-file-name: + type: str + pac_file_name: description: - Pac file name. + type: str policyid: description: - Policy ID. required: true + type: int srcaddr: description: - Source address objects. + type: list suboptions: name: description: - Address name. Source firewall.address.name firewall.addrgrp.name firewall.proxy-address.name firewall.proxy-addrgrp.name. required: true + type: str srcaddr6: description: - Source address6 objects. + type: list suboptions: name: description: - Address name. Source firewall.address6.name firewall.addrgrp6.name. required: true + type: str status: description: - Enable/disable policy. + type: str choices: - enable - disable - pref-dns-result: + pref_dns_result: description: - - Prefer resolving addresses using the configured IPv4 or IPv6 DNS server (default = ipv4). + - Prefer resolving addresses using the configured IPv4 or IPv6 DNS server . + type: str choices: - ipv4 - ipv6 realm: description: - Authentication realm used to identify the explicit web proxy (maximum of 63 characters). - sec-default-action: + type: str + sec_default_action: description: - Accept or deny explicit web proxy sessions when no web proxy firewall policy exists. + type: str choices: - accept - deny socks: description: - Enable/disable the SOCKS proxy. + type: str choices: - enable - disable - socks-incoming-port: + socks_incoming_port: description: - - Accept incoming SOCKS proxy requests on one or more ports (0 - 65535, default = 0; use the same as HTTP). - ssl-algorithm: + - Accept incoming SOCKS proxy requests on one or more ports (0 - 65535). + type: str + ssl_algorithm: description: - "Relative strength of encryption algorithms accepted in HTTPS deep scan: high, medium, or low." + type: str choices: - low status: description: - Enable/disable the explicit Web proxy for HTTP and HTTPS session. + type: str choices: - enable - disable - strict-guest: + strict_guest: description: - Enable/disable strict guest user checking by the explicit web proxy. + type: str choices: - enable - disable - trace-auth-no-rsp: + trace_auth_no_rsp: description: - Enable/disable logging timed-out authentication requests. + type: str choices: - enable - disable - unknown-http-version: + unknown_http_version: description: - Either reject unknown HTTP traffic as malformed or handle unknown HTTP traffic as best as the proxy server can. + type: str choices: - reject - best-effort @@ -242,6 +289,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure explicit Web proxy settings. fortios_web_proxy_explicit: @@ -251,30 +299,30 @@ EXAMPLES = ''' vdom: "{{ vdom }}" https: "False" web_proxy_explicit: - ftp-incoming-port: "" - ftp-over-http: "enable" - http-incoming-port: "" - https-incoming-port: "" - https-replacement-message: "enable" - incoming-ip: "" - incoming-ip6: "" - ipv6-status: "enable" - message-upon-server-error: "enable" - outgoing-ip: "" - outgoing-ip6: "" - pac-file-data: "" - pac-file-name: "" - pac-file-server-port: "" - pac-file-server-status: "enable" - pac-file-url: "" - pac-policy: + ftp_incoming_port: "" + ftp_over_http: "enable" + http_incoming_port: "" + https_incoming_port: "" + https_replacement_message: "enable" + incoming_ip: "" + incoming_ip6: "" + ipv6_status: "enable" + message_upon_server_error: "enable" + outgoing_ip: "" + outgoing_ip6: "" + pac_file_data: "" + pac_file_name: "" + pac_file_server_port: "" + pac_file_server_status: "enable" + pac_file_url: "" + pac_policy: - comments: "" dstaddr: - name: "default_name_22 (source firewall.address.name firewall.addrgrp.name)" - pac-file-data: "" - pac-file-name: "" + pac_file_data: "" + pac_file_name: "" policyid: "25" srcaddr: - @@ -283,16 +331,16 @@ EXAMPLES = ''' - name: "default_name_29 (source firewall.address6.name firewall.addrgrp6.name)" status: "enable" - pref-dns-result: "ipv4" + pref_dns_result: "ipv4" realm: "" - sec-default-action: "accept" + sec_default_action: "accept" socks: "enable" - socks-incoming-port: "" - ssl-algorithm: "low" + socks_incoming_port: "" + ssl_algorithm: "low" status: "enable" - strict-guest: "enable" - trace-auth-no-rsp: "enable" - unknown-http-version: "reject" + strict_guest: "enable" + trace_auth_no_rsp: "enable" + unknown_http_version: "reject" ''' RETURN = ''' @@ -355,12 +403,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 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']: @@ -368,19 +420,19 @@ def login(data, fos): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_web_proxy_explicit_data(json): - option_list = ['ftp-incoming-port', 'ftp-over-http', 'http-incoming-port', - 'https-incoming-port', 'https-replacement-message', 'incoming-ip', - 'incoming-ip6', 'ipv6-status', 'message-upon-server-error', - 'outgoing-ip', 'outgoing-ip6', 'pac-file-data', - 'pac-file-name', 'pac-file-server-port', 'pac-file-server-status', - 'pac-file-url', 'pac-policy', 'pref-dns-result', - 'realm', 'sec-default-action', 'socks', - 'socks-incoming-port', 'ssl-algorithm', 'status', - 'strict-guest', 'trace-auth-no-rsp', 'unknown-http-version'] + option_list = ['ftp_incoming_port', 'ftp_over_http', 'http_incoming_port', + 'https_incoming_port', 'https_replacement_message', 'incoming_ip', + 'incoming_ip6', 'ipv6_status', 'message_upon_server_error', + 'outgoing_ip', 'outgoing_ip6', 'pac_file_data', + 'pac_file_name', 'pac_file_server_port', 'pac_file_server_status', + 'pac_file_url', 'pac_policy', 'pref_dns_result', + 'realm', 'sec_default_action', 'socks', + 'socks_incoming_port', 'ssl_algorithm', 'status', + 'strict_guest', 'trace_auth_no_rsp', 'unknown_http_version'] dictionary = {} for attribute in option_list: @@ -390,10 +442,23 @@ def filter_web_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 web_proxy_explicit(data, fos): vdom = data['vdom'] web_proxy_explicit_data = data['web_proxy_explicit'] - filtered_data = filter_web_proxy_explicit_data(web_proxy_explicit_data) + filtered_data = underscore_to_hyphen(filter_web_proxy_explicit_data(web_proxy_explicit_data)) return fos.set('web-proxy', 'explicit', @@ -401,56 +466,62 @@ def web_proxy_explicit(data, fos): vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_web_proxy(data, fos): - login(data, fos) if data['web_proxy_explicit']: resp = web_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}, "web_proxy_explicit": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "ftp-incoming-port": {"required": False, "type": "str"}, - "ftp-over-http": {"required": False, "type": "str", + "ftp_incoming_port": {"required": False, "type": "str"}, + "ftp_over_http": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "http-incoming-port": {"required": False, "type": "str"}, - "https-incoming-port": {"required": False, "type": "str"}, - "https-replacement-message": {"required": False, "type": "str", + "http_incoming_port": {"required": False, "type": "str"}, + "https_incoming_port": {"required": False, "type": "str"}, + "https_replacement_message": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "incoming-ip": {"required": False, "type": "str"}, - "incoming-ip6": {"required": False, "type": "str"}, - "ipv6-status": {"required": False, "type": "str", + "incoming_ip": {"required": False, "type": "str"}, + "incoming_ip6": {"required": False, "type": "str"}, + "ipv6_status": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "message-upon-server-error": {"required": False, "type": "str", + "message_upon_server_error": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "outgoing-ip": {"required": False, "type": "str"}, - "outgoing-ip6": {"required": False, "type": "str"}, - "pac-file-data": {"required": False, "type": "str"}, - "pac-file-name": {"required": False, "type": "str"}, - "pac-file-server-port": {"required": False, "type": "str"}, - "pac-file-server-status": {"required": False, "type": "str", + "outgoing_ip": {"required": False, "type": "str"}, + "outgoing_ip6": {"required": False, "type": "str"}, + "pac_file_data": {"required": False, "type": "str"}, + "pac_file_name": {"required": False, "type": "str"}, + "pac_file_server_port": {"required": False, "type": "str"}, + "pac_file_server_status": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "pac-file-url": {"required": False, "type": "str"}, - "pac-policy": {"required": False, "type": "list", + "pac_file_url": {"required": False, "type": "str"}, + "pac_policy": {"required": False, "type": "list", "options": { "comments": {"required": False, "type": "str"}, "dstaddr": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, - "pac-file-data": {"required": False, "type": "str"}, - "pac-file-name": {"required": False, "type": "str"}, + "pac_file_data": {"required": False, "type": "str"}, + "pac_file_name": {"required": False, "type": "str"}, "policyid": {"required": True, "type": "int"}, "srcaddr": {"required": False, "type": "list", "options": { @@ -463,23 +534,23 @@ def main(): "status": {"required": False, "type": "str", "choices": ["enable", "disable"]} }}, - "pref-dns-result": {"required": False, "type": "str", + "pref_dns_result": {"required": False, "type": "str", "choices": ["ipv4", "ipv6"]}, "realm": {"required": False, "type": "str"}, - "sec-default-action": {"required": False, "type": "str", + "sec_default_action": {"required": False, "type": "str", "choices": ["accept", "deny"]}, "socks": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "socks-incoming-port": {"required": False, "type": "str"}, - "ssl-algorithm": {"required": False, "type": "str", + "socks_incoming_port": {"required": False, "type": "str"}, + "ssl_algorithm": {"required": False, "type": "str", "choices": ["low"]}, "status": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "strict-guest": {"required": False, "type": "str", + "strict_guest": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "trace-auth-no-rsp": {"required": False, "type": "str", + "trace_auth_no_rsp": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "unknown-http-version": {"required": False, "type": "str", + "unknown_http_version": {"required": False, "type": "str", "choices": ["reject", "best-effort"]} } @@ -488,14 +559,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") - 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_web_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_web_proxy(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_web_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_web_proxy_global.py b/lib/ansible/modules/network/fortios/fortios_web_proxy_global.py index 92bd84e2946..a8721bcc003 100644 --- a/lib/ansible/modules/network/fortios/fortios_web_proxy_global.py +++ b/lib/ansible/modules/network/fortios/fortios_web_proxy_global.py @@ -26,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_web_proxy_global short_description: Configure Web proxy global settings in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify web_proxy feature and global category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -41,113 +41,140 @@ 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: true + version_added: 2.9 web_proxy_global: description: - Configure Web proxy global settings. default: null + type: dict suboptions: - fast-policy-match: + fast_policy_match: description: - Enable/disable fast matching algorithm for explicit and transparent proxy policy. + type: str choices: - enable - disable - forward-proxy-auth: + forward_proxy_auth: description: - Enable/disable forwarding proxy authentication headers. + type: str choices: - enable - disable - forward-server-affinity-timeout: + forward_server_affinity_timeout: description: - - Period of time before the source IP's traffic is no longer assigned to the forwarding server (6 - 60 min, default = 30). - learn-client-ip: + - Period of time before the source IP's traffic is no longer assigned to the forwarding server (6 - 60 min). + type: int + learn_client_ip: description: - Enable/disable learning the client's IP address from headers. + type: str choices: - enable - disable - learn-client-ip-from-header: + learn_client_ip_from_header: description: - Learn client IP address from the specified headers. + type: str choices: - true-client-ip - x-real-ip - x-forwarded-for - learn-client-ip-srcaddr: + learn_client_ip_srcaddr: description: - Source address name (srcaddr or srcaddr6 must be set). + type: list suboptions: name: description: - Address name. Source firewall.address.name firewall.addrgrp.name. required: true - learn-client-ip-srcaddr6: + type: str + learn_client_ip_srcaddr6: description: - IPv6 Source address name (srcaddr or srcaddr6 must be set). + type: list suboptions: name: description: - Address name. Source firewall.address6.name firewall.addrgrp6.name. required: true - max-message-length: + type: str + max_message_length: description: - - Maximum length of HTTP message, not including body (16 - 256 Kbytes, default = 32). - max-request-length: + - Maximum length of HTTP message, not including body (16 - 256 Kbytes). + type: int + max_request_length: description: - - Maximum length of HTTP request line (2 - 64 Kbytes, default = 4). - max-waf-body-cache-length: + - Maximum length of HTTP request line (2 - 64 Kbytes). + type: int + max_waf_body_cache_length: description: - - Maximum length of HTTP messages processed by Web Application Firewall (WAF) (10 - 1024 Kbytes, default = 32). - proxy-fqdn: + - Maximum length of HTTP messages processed by Web Application Firewall (WAF) (10 - 1024 Kbytes). + type: int + proxy_fqdn: description: - - Fully Qualified Domain Name (FQDN) that clients connect to (default = default.fqdn) to connect to the explicit web proxy. - strict-web-check: + - Fully Qualified Domain Name (FQDN) that clients connect to to connect to the explicit web proxy. + type: str + strict_web_check: description: - Enable/disable strict web checking to block web sites that send incorrect headers that don't conform to HTTP 1.1. + type: str choices: - enable - disable - tunnel-non-http: + tunnel_non_http: description: - Enable/disable allowing non-HTTP traffic. Allowed non-HTTP traffic is tunneled. + type: str choices: - enable - disable - unknown-http-version: + unknown_http_version: description: - "Action to take when an unknown version of HTTP is encountered: reject, allow (tunnel), or proceed with best-effort." + type: str choices: - reject - tunnel - best-effort - webproxy-profile: + webproxy_profile: description: - Name of the web proxy profile to apply when explicit proxy traffic is allowed by default and traffic is accepted that does not match an explicit proxy policy. Source web-proxy.profile.name. + type: str ''' EXAMPLES = ''' @@ -157,6 +184,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure Web proxy global settings. fortios_web_proxy_global: @@ -166,25 +194,25 @@ EXAMPLES = ''' vdom: "{{ vdom }}" https: "False" web_proxy_global: - fast-policy-match: "enable" - forward-proxy-auth: "enable" - forward-server-affinity-timeout: "5" - learn-client-ip: "enable" - learn-client-ip-from-header: "true-client-ip" - learn-client-ip-srcaddr: + fast_policy_match: "enable" + forward_proxy_auth: "enable" + forward_server_affinity_timeout: "5" + learn_client_ip: "enable" + learn_client_ip_from_header: "true-client-ip" + learn_client_ip_srcaddr: - name: "default_name_9 (source firewall.address.name firewall.addrgrp.name)" - learn-client-ip-srcaddr6: + learn_client_ip_srcaddr6: - name: "default_name_11 (source firewall.address6.name firewall.addrgrp6.name)" - max-message-length: "12" - max-request-length: "13" - max-waf-body-cache-length: "14" - proxy-fqdn: "" - strict-web-check: "enable" - tunnel-non-http: "enable" - unknown-http-version: "reject" - webproxy-profile: " (source web-proxy.profile.name)" + max_message_length: "12" + max_request_length: "13" + max_waf_body_cache_length: "14" + proxy_fqdn: "" + strict_web_check: "enable" + tunnel_non_http: "enable" + unknown_http_version: "reject" + webproxy_profile: " (source web-proxy.profile.name)" ''' RETURN = ''' @@ -247,12 +275,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 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']: @@ -260,15 +292,15 @@ def login(data, fos): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_web_proxy_global_data(json): - option_list = ['fast-policy-match', 'forward-proxy-auth', 'forward-server-affinity-timeout', - 'learn-client-ip', 'learn-client-ip-from-header', 'learn-client-ip-srcaddr', - 'learn-client-ip-srcaddr6', 'max-message-length', 'max-request-length', - 'max-waf-body-cache-length', 'proxy-fqdn', 'strict-web-check', - 'tunnel-non-http', 'unknown-http-version', 'webproxy-profile'] + option_list = ['fast_policy_match', 'forward_proxy_auth', 'forward_server_affinity_timeout', + 'learn_client_ip', 'learn_client_ip_from_header', 'learn_client_ip_srcaddr', + 'learn_client_ip_srcaddr6', 'max_message_length', 'max_request_length', + 'max_waf_body_cache_length', 'proxy_fqdn', 'strict_web_check', + 'tunnel_non_http', 'unknown_http_version', 'webproxy_profile'] dictionary = {} for attribute in option_list: @@ -278,10 +310,23 @@ def filter_web_proxy_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 web_proxy_global(data, fos): vdom = data['vdom'] web_proxy_global_data = data['web_proxy_global'] - filtered_data = filter_web_proxy_global_data(web_proxy_global_data) + filtered_data = underscore_to_hyphen(filter_web_proxy_global_data(web_proxy_global_data)) return fos.set('web-proxy', 'global', @@ -289,54 +334,60 @@ def web_proxy_global(data, fos): vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_web_proxy(data, fos): - login(data, fos) if data['web_proxy_global']: resp = web_proxy_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}, "web_proxy_global": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "fast-policy-match": {"required": False, "type": "str", + "fast_policy_match": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "forward-proxy-auth": {"required": False, "type": "str", + "forward_proxy_auth": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "forward-server-affinity-timeout": {"required": False, "type": "int"}, - "learn-client-ip": {"required": False, "type": "str", + "forward_server_affinity_timeout": {"required": False, "type": "int"}, + "learn_client_ip": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "learn-client-ip-from-header": {"required": False, "type": "str", + "learn_client_ip_from_header": {"required": False, "type": "str", "choices": ["true-client-ip", "x-real-ip", "x-forwarded-for"]}, - "learn-client-ip-srcaddr": {"required": False, "type": "list", + "learn_client_ip_srcaddr": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, - "learn-client-ip-srcaddr6": {"required": False, "type": "list", + "learn_client_ip_srcaddr6": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, - "max-message-length": {"required": False, "type": "int"}, - "max-request-length": {"required": False, "type": "int"}, - "max-waf-body-cache-length": {"required": False, "type": "int"}, - "proxy-fqdn": {"required": False, "type": "str"}, - "strict-web-check": {"required": False, "type": "str", + "max_message_length": {"required": False, "type": "int"}, + "max_request_length": {"required": False, "type": "int"}, + "max_waf_body_cache_length": {"required": False, "type": "int"}, + "proxy_fqdn": {"required": False, "type": "str"}, + "strict_web_check": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "tunnel-non-http": {"required": False, "type": "str", + "tunnel_non_http": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "unknown-http-version": {"required": False, "type": "str", + "unknown_http_version": {"required": False, "type": "str", "choices": ["reject", "tunnel", "best-effort"]}, - "webproxy-profile": {"required": False, "type": "str"} + "webproxy_profile": {"required": False, "type": "str"} } } @@ -344,14 +395,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") - 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_web_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_web_proxy(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_web_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_web_proxy_profile.py b/lib/ansible/modules/network/fortios/fortios_web_proxy_profile.py index 6f397316aad..8c12d33baf0 100644 --- a/lib/ansible/modules/network/fortios/fortios_web_proxy_profile.py +++ b/lib/ansible/modules/network/fortios/fortios_web_proxy_profile.py @@ -26,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_web_proxy_profile short_description: Configure web proxy profiles in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify web_proxy feature and profile category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -41,85 +41,105 @@ 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: 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 web_proxy_profile: description: - Configure web proxy profiles. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent - header-client-ip: + header_client_ip: description: - "Action to take on the HTTP client-IP header in forwarded requests: forwards (pass), adds, or removes the HTTP header." + type: str choices: - pass - add - remove - header-front-end-https: + header_front_end_https: description: - "Action to take on the HTTP front-end-HTTPS header in forwarded requests: forwards (pass), adds, or removes the HTTP header." + type: str choices: - pass - add - remove - header-via-request: + header_via_request: description: - "Action to take on the HTTP via header in forwarded requests: forwards (pass), adds, or removes the HTTP header." + type: str choices: - pass - add - remove - header-via-response: + header_via_response: description: - "Action to take on the HTTP via header in forwarded responses: forwards (pass), adds, or removes the HTTP header." + type: str choices: - pass - add - remove - header-x-authenticated-groups: + header_x_authenticated_groups: description: - "Action to take on the HTTP x-authenticated-groups header in forwarded requests: forwards (pass), adds, or removes the HTTP header." + type: str choices: - pass - add - remove - header-x-authenticated-user: + header_x_authenticated_user: description: - "Action to take on the HTTP x-authenticated-user header in forwarded requests: forwards (pass), adds, or removes the HTTP header." + type: str choices: - pass - add - remove - header-x-forwarded-for: + header_x_forwarded_for: description: - "Action to take on the HTTP x-forwarded-for header in forwarded requests: forwards (pass), adds, or removes the HTTP header." + type: str choices: - pass - add @@ -127,10 +147,12 @@ options: headers: description: - Configure HTTP forwarded requests headers. + type: list suboptions: action: description: - Action when HTTP the header forwarded. + type: str choices: - add-to-request - add-to-response @@ -139,16 +161,20 @@ options: content: description: - HTTP header's content. + type: str id: description: - HTTP forwarded header id. required: true + type: int name: description: - HTTP forwarded header name. - log-header-change: + type: str + log_header_change: description: - Enable/disable logging HTTP header changes. + type: str choices: - enable - disable @@ -156,9 +182,11 @@ options: description: - Profile name. required: true - strip-encoding: + type: str + strip_encoding: description: - Enable/disable stripping unsupported encoding from the request header. + type: str choices: - enable - disable @@ -171,6 +199,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure web proxy profiles. fortios_web_proxy_profile: @@ -179,24 +208,24 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" web_proxy_profile: - state: "present" - header-client-ip: "pass" - header-front-end-https: "pass" - header-via-request: "pass" - header-via-response: "pass" - header-x-authenticated-groups: "pass" - header-x-authenticated-user: "pass" - header-x-forwarded-for: "pass" + header_client_ip: "pass" + header_front_end_https: "pass" + header_via_request: "pass" + header_via_response: "pass" + header_x_authenticated_groups: "pass" + header_x_authenticated_user: "pass" + header_x_forwarded_for: "pass" headers: - action: "add-to-request" content: "" id: "13" name: "default_name_14" - log-header-change: "enable" + log_header_change: "enable" name: "default_name_16" - strip-encoding: "enable" + strip_encoding: "enable" ''' RETURN = ''' @@ -259,12 +288,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 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']: @@ -272,14 +305,14 @@ def login(data, fos): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_web_proxy_profile_data(json): - option_list = ['header-client-ip', 'header-front-end-https', 'header-via-request', - 'header-via-response', 'header-x-authenticated-groups', 'header-x-authenticated-user', - 'header-x-forwarded-for', 'headers', 'log-header-change', - 'name', 'strip-encoding'] + option_list = ['header_client_ip', 'header_front_end_https', 'header_via_request', + 'header_via_response', 'header_x_authenticated_groups', 'header_x_authenticated_user', + 'header_x_forwarded_for', 'headers', 'log_header_change', + 'name', 'strip_encoding'] dictionary = {} for attribute in option_list: @@ -289,59 +322,79 @@ def filter_web_proxy_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 web_proxy_profile(data, fos): vdom = data['vdom'] + state = data['state'] web_proxy_profile_data = data['web_proxy_profile'] - filtered_data = filter_web_proxy_profile_data(web_proxy_profile_data) + filtered_data = underscore_to_hyphen(filter_web_proxy_profile_data(web_proxy_profile_data)) - if web_proxy_profile_data['state'] == "present": + if state == "present": return fos.set('web-proxy', 'profile', data=filtered_data, vdom=vdom) - elif web_proxy_profile_data['state'] == "absent": + elif state == "absent": return fos.delete('web-proxy', '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_web_proxy(data, fos): - login(data, fos) if data['web_proxy_profile']: resp = web_proxy_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"]}, "web_proxy_profile": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, - "header-client-ip": {"required": False, "type": "str", + "header_client_ip": {"required": False, "type": "str", "choices": ["pass", "add", "remove"]}, - "header-front-end-https": {"required": False, "type": "str", + "header_front_end_https": {"required": False, "type": "str", "choices": ["pass", "add", "remove"]}, - "header-via-request": {"required": False, "type": "str", + "header_via_request": {"required": False, "type": "str", "choices": ["pass", "add", "remove"]}, - "header-via-response": {"required": False, "type": "str", + "header_via_response": {"required": False, "type": "str", "choices": ["pass", "add", "remove"]}, - "header-x-authenticated-groups": {"required": False, "type": "str", + "header_x_authenticated_groups": {"required": False, "type": "str", "choices": ["pass", "add", "remove"]}, - "header-x-authenticated-user": {"required": False, "type": "str", + "header_x_authenticated_user": {"required": False, "type": "str", "choices": ["pass", "add", "remove"]}, - "header-x-forwarded-for": {"required": False, "type": "str", + "header_x_forwarded_for": {"required": False, "type": "str", "choices": ["pass", "add", "remove"]}, "headers": {"required": False, "type": "list", "options": { @@ -352,10 +405,10 @@ def main(): "id": {"required": True, "type": "int"}, "name": {"required": False, "type": "str"} }}, - "log-header-change": {"required": False, "type": "str", + "log_header_change": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "name": {"required": True, "type": "str"}, - "strip-encoding": {"required": False, "type": "str", + "strip_encoding": {"required": False, "type": "str", "choices": ["enable", "disable"]} } @@ -364,14 +417,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") - 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_web_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_web_proxy(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_web_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_webfilter_fortiguard.py b/lib/ansible/modules/network/fortios/fortios_webfilter_fortiguard.py index 5f8d2d5a4e6..0f2ff208ef5 100644 --- a/lib/ansible/modules/network/fortios/fortios_webfilter_fortiguard.py +++ b/lib/ansible/modules/network/fortios/fortios_webfilter_fortiguard.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 @@ -27,12 +24,12 @@ ANSIBLE_METADATA = {'status': ['preview'], DOCUMENTATION = ''' --- module: fortios_webfilter_fortiguard -short_description: Configure FortiGuard Web Filter service. +short_description: Configure FortiGuard Web Filter service in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure webfilter feature and fortiguard 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 webfilter feature and fortiguard 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,79 +41,100 @@ 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 webfilter_fortiguard: description: - Configure FortiGuard Web Filter service. default: null + type: dict suboptions: - cache-mem-percent: + cache_mem_percent: description: - Maximum percentage of available memory allocated to caching (1 - 15%). - cache-mode: + type: int + cache_mode: description: - Cache entry expiration mode. + type: str choices: - ttl - db-ver - cache-prefix-match: + cache_prefix_match: description: - Enable/disable prefix matching in the cache. + type: str choices: - enable - disable - close-ports: + close_ports: description: - Close ports used for HTTP/HTTPS override authentication and disable user overrides. + type: str choices: - enable - disable - ovrd-auth-https: + ovrd_auth_https: description: - Enable/disable use of HTTPS for override authentication. + type: str choices: - enable - disable - ovrd-auth-port: + ovrd_auth_port: description: - Port to use for FortiGuard Web Filter override authentication. - ovrd-auth-port-http: + type: int + ovrd_auth_port_http: description: - Port to use for FortiGuard Web Filter HTTP override authentication - ovrd-auth-port-https: + type: int + ovrd_auth_port_https: description: - Port to use for FortiGuard Web Filter HTTPS override authentication. - ovrd-auth-port-warning: + type: int + ovrd_auth_port_warning: description: - Port to use for FortiGuard Web Filter Warning override authentication. - request-packet-size-limit: + type: int + request_packet_size_limit: description: - Limit size of URL request packets sent to FortiGuard server (0 for default). - warn-auth-https: + type: int + warn_auth_https: description: - Enable/disable use of HTTPS for warning and authentication. + type: str choices: - enable - disable @@ -129,25 +147,27 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure FortiGuard Web Filter service. fortios_webfilter_fortiguard: - host: "{{ host }}" + host: "{{ host }}" username: "{{ username }}" password: "{{ password }}" - vdom: "{{ vdom }}" + vdom: "{{ vdom }}" + https: "False" webfilter_fortiguard: - cache-mem-percent: "3" - cache-mode: "ttl" - cache-prefix-match: "enable" - close-ports: "enable" - ovrd-auth-https: "enable" - ovrd-auth-port: "8" - ovrd-auth-port-http: "9" - ovrd-auth-port-https: "10" - ovrd-auth-port-warning: "11" - request-packet-size-limit: "12" - warn-auth-https: "enable" + cache_mem_percent: "3" + cache_mode: "ttl" + cache_prefix_match: "enable" + close_ports: "enable" + ovrd_auth_https: "enable" + ovrd_auth_port: "8" + ovrd_auth_port_http: "9" + ovrd_auth_port_https: "10" + ovrd_auth_port_warning: "11" + request_packet_size_limit: "12" + warn_auth_https: "enable" ''' RETURN = ''' @@ -170,7 +190,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 @@ -210,14 +230,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']: @@ -225,75 +247,88 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_webfilter_fortiguard_data(json): - option_list = ['cache-mem-percent', 'cache-mode', 'cache-prefix-match', - 'close-ports', 'ovrd-auth-https', 'ovrd-auth-port', - 'ovrd-auth-port-http', 'ovrd-auth-port-https', 'ovrd-auth-port-warning', - 'request-packet-size-limit', 'warn-auth-https'] + option_list = ['cache_mem_percent', 'cache_mode', 'cache_prefix_match', + 'close_ports', 'ovrd_auth_https', 'ovrd_auth_port', + 'ovrd_auth_port_http', 'ovrd_auth_port_https', 'ovrd_auth_port_warning', + 'request_packet_size_limit', 'warn_auth_https'] dictionary = {} for attribute in option_list: - if attribute in json: + if attribute in json and json[attribute] is not None: dictionary[attribute] = json[attribute] 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 webfilter_fortiguard(data, fos): vdom = data['vdom'] webfilter_fortiguard_data = data['webfilter_fortiguard'] - filtered_data = filter_webfilter_fortiguard_data(webfilter_fortiguard_data) + filtered_data = underscore_to_hyphen(filter_webfilter_fortiguard_data(webfilter_fortiguard_data)) + return fos.set('webfilter', 'fortiguard', 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_webfilter(data, fos): - host = data['host'] - username = data['username'] - password = data['password'] - fos.https('off') - fos.login(host, username, password) - methodlist = ['webfilter_fortiguard'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['webfilter_fortiguard']: + resp = webfilter_fortiguard(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}, "webfilter_fortiguard": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "cache-mem-percent": {"required": False, "type": "int"}, - "cache-mode": {"required": False, "type": "str", + "cache_mem_percent": {"required": False, "type": "int"}, + "cache_mode": {"required": False, "type": "str", "choices": ["ttl", "db-ver"]}, - "cache-prefix-match": {"required": False, "type": "str", + "cache_prefix_match": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "close-ports": {"required": False, "type": "str", + "close_ports": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ovrd-auth-https": {"required": False, "type": "str", + "ovrd_auth_https": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ovrd-auth-port": {"required": False, "type": "int"}, - "ovrd-auth-port-http": {"required": False, "type": "int"}, - "ovrd-auth-port-https": {"required": False, "type": "int"}, - "ovrd-auth-port-warning": {"required": False, "type": "int"}, - "request-packet-size-limit": {"required": False, "type": "int"}, - "warn-auth-https": {"required": False, "type": "str", + "ovrd_auth_port": {"required": False, "type": "int"}, + "ovrd_auth_port_http": {"required": False, "type": "int"}, + "ovrd_auth_port_https": {"required": False, "type": "int"}, + "ovrd_auth_port_warning": {"required": False, "type": "int"}, + "request_packet_size_limit": {"required": False, "type": "int"}, + "warn_auth_https": {"required": False, "type": "str", "choices": ["enable", "disable"]} } @@ -302,14 +337,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") - 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_webfilter(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_webfilter(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_webfilter(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_webfilter_ftgd_local_cat.py b/lib/ansible/modules/network/fortios/fortios_webfilter_ftgd_local_cat.py index 0642c7c88c7..e862a3be4d8 100644 --- a/lib/ansible/modules/network/fortios/fortios_webfilter_ftgd_local_cat.py +++ b/lib/ansible/modules/network/fortios/fortios_webfilter_ftgd_local_cat.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 @@ -27,12 +24,12 @@ ANSIBLE_METADATA = {'status': ['preview'], DOCUMENTATION = ''' --- module: fortios_webfilter_ftgd_local_cat -short_description: Configure FortiGuard Web Filter local categories. +short_description: Configure FortiGuard Web Filter local categories in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure webfilter feature and ftgd_local_cat 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 webfilter feature and ftgd_local_cat 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,50 +41,66 @@ 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 webfilter_ftgd_local_cat: description: - Configure FortiGuard Web Filter local categories. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent desc: description: - Local category description. required: true + type: str id: description: - Local category ID. + type: int status: description: - Enable/disable the local category. + type: str choices: - enable - disable @@ -100,6 +113,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure FortiGuard Web Filter local categories. fortios_webfilter_ftgd_local_cat: @@ -107,8 +121,9 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" + state: "present" webfilter_ftgd_local_cat: - state: "present" desc: "" id: "4" status: "enable" @@ -134,7 +149,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 @@ -174,14 +189,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']: @@ -189,7 +206,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_webfilter_ftgd_local_cat_data(json): @@ -197,55 +214,72 @@ def filter_webfilter_ftgd_local_cat_data(json): dictionary = {} for attribute in option_list: - if attribute in json: + if attribute in json and json[attribute] is not None: dictionary[attribute] = json[attribute] 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 webfilter_ftgd_local_cat(data, fos): vdom = data['vdom'] + state = data['state'] webfilter_ftgd_local_cat_data = data['webfilter_ftgd_local_cat'] - filtered_data = filter_webfilter_ftgd_local_cat_data( - webfilter_ftgd_local_cat_data) - if webfilter_ftgd_local_cat_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_webfilter_ftgd_local_cat_data(webfilter_ftgd_local_cat_data)) + + if state == "present": return fos.set('webfilter', 'ftgd-local-cat', data=filtered_data, vdom=vdom) - elif webfilter_ftgd_local_cat_data['state'] == "absent": + elif state == "absent": return fos.delete('webfilter', 'ftgd-local-cat', mkey=filtered_data['desc'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_webfilter(data, fos): - login(data) - methodlist = ['webfilter_ftgd_local_cat'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['webfilter_ftgd_local_cat']: + resp = webfilter_ftgd_local_cat(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"]}, "webfilter_ftgd_local_cat": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "desc": {"required": True, "type": "str"}, "id": {"required": False, "type": "int"}, "status": {"required": False, "type": "str", @@ -257,15 +291,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_webfilter(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_webfilter(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_webfilter(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_webfilter_ftgd_local_rating.py b/lib/ansible/modules/network/fortios/fortios_webfilter_ftgd_local_rating.py index 379ba5e903a..b44d0efb6af 100644 --- a/lib/ansible/modules/network/fortios/fortios_webfilter_ftgd_local_rating.py +++ b/lib/ansible/modules/network/fortios/fortios_webfilter_ftgd_local_rating.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 @@ -27,12 +24,12 @@ ANSIBLE_METADATA = {'status': ['preview'], DOCUMENTATION = ''' --- module: fortios_webfilter_ftgd_local_rating -short_description: Configure local FortiGuard Web Filter local ratings. +short_description: Configure local FortiGuard Web Filter local ratings in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure webfilter feature and ftgd_local_rating 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 webfilter feature and ftgd_local_rating 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,46 +41,61 @@ 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 webfilter_ftgd_local_rating: description: - Configure local FortiGuard Web Filter local ratings. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent rating: description: - Local rating. + type: str status: description: - Enable/disable local rating. + type: str choices: - enable - disable @@ -91,6 +103,7 @@ options: description: - URL to rate locally. required: true + type: str ''' EXAMPLES = ''' @@ -100,6 +113,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure local FortiGuard Web Filter local ratings. fortios_webfilter_ftgd_local_rating: @@ -107,8 +121,9 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" + state: "present" webfilter_ftgd_local_rating: - state: "present" rating: "" status: "enable" url: "myurl.com" @@ -134,7 +149,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 @@ -174,14 +189,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']: @@ -189,7 +206,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_webfilter_ftgd_local_rating_data(json): @@ -197,55 +214,72 @@ def filter_webfilter_ftgd_local_rating_data(json): dictionary = {} for attribute in option_list: - if attribute in json: + if attribute in json and json[attribute] is not None: dictionary[attribute] = json[attribute] 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 webfilter_ftgd_local_rating(data, fos): vdom = data['vdom'] + state = data['state'] webfilter_ftgd_local_rating_data = data['webfilter_ftgd_local_rating'] - filtered_data = filter_webfilter_ftgd_local_rating_data( - webfilter_ftgd_local_rating_data) - if webfilter_ftgd_local_rating_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_webfilter_ftgd_local_rating_data(webfilter_ftgd_local_rating_data)) + + if state == "present": return fos.set('webfilter', 'ftgd-local-rating', data=filtered_data, vdom=vdom) - elif webfilter_ftgd_local_rating_data['state'] == "absent": + elif state == "absent": return fos.delete('webfilter', 'ftgd-local-rating', mkey=filtered_data['url'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_webfilter(data, fos): - login(data) - methodlist = ['webfilter_ftgd_local_rating'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['webfilter_ftgd_local_rating']: + resp = webfilter_ftgd_local_rating(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"]}, "webfilter_ftgd_local_rating": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "rating": {"required": False, "type": "str"}, "status": {"required": False, "type": "str", "choices": ["enable", "disable"]}, @@ -257,15 +291,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_webfilter(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_webfilter(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_webfilter(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_webfilter_ips_urlfilter_cache_setting.py b/lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_cache_setting.py index 69dbbcc06a2..baf72727ec0 100644 --- a/lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_cache_setting.py +++ b/lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_cache_setting.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 @@ -27,12 +24,12 @@ ANSIBLE_METADATA = {'status': ['preview'], DOCUMENTATION = ''' --- module: fortios_webfilter_ips_urlfilter_cache_setting -short_description: Configure IPS URL filter cache settings. +short_description: Configure IPS URL filter cache settings in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure webfilter feature and ips_urlfilter_cache_setting 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 webfilter feature and ips_urlfilter_cache_setting 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,40 +41,52 @@ 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 webfilter_ips_urlfilter_cache_setting: description: - Configure IPS URL filter cache settings. default: null + type: dict suboptions: - dns-retry-interval: + dns_retry_interval: description: - Retry interval. Refresh DNS faster than TTL to capture multiple IPs for hosts. 0 means use DNS server's TTL only. - extended-ttl: + type: int + extended_ttl: description: - Extend time to live beyond reported by DNS. 0 means use DNS server's TTL + type: int ''' EXAMPLES = ''' @@ -87,6 +96,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPS URL filter cache settings. fortios_webfilter_ips_urlfilter_cache_setting: @@ -94,9 +104,10 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" webfilter_ips_urlfilter_cache_setting: - dns-retry-interval: "3" - extended-ttl: "4" + dns_retry_interval: "3" + extended_ttl: "4" ''' RETURN = ''' @@ -119,7 +130,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 @@ -159,14 +170,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']: @@ -174,11 +187,11 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_webfilter_ips_urlfilter_cache_setting_data(json): - option_list = ['dns-retry-interval', 'extended-ttl'] + option_list = ['dns_retry_interval', 'extended_ttl'] dictionary = {} for attribute in option_list: @@ -188,42 +201,58 @@ def filter_webfilter_ips_urlfilter_cache_setting_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 webfilter_ips_urlfilter_cache_setting(data, fos): vdom = data['vdom'] webfilter_ips_urlfilter_cache_setting_data = data['webfilter_ips_urlfilter_cache_setting'] - filtered_data = filter_webfilter_ips_urlfilter_cache_setting_data( - webfilter_ips_urlfilter_cache_setting_data) + filtered_data = underscore_to_hyphen(filter_webfilter_ips_urlfilter_cache_setting_data(webfilter_ips_urlfilter_cache_setting_data)) + return fos.set('webfilter', 'ips-urlfilter-cache-setting', 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_webfilter(data, fos): - login(data) - methodlist = ['webfilter_ips_urlfilter_cache_setting'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['webfilter_ips_urlfilter_cache_setting']: + resp = webfilter_ips_urlfilter_cache_setting(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}, "webfilter_ips_urlfilter_cache_setting": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "dns-retry-interval": {"required": False, "type": "int"}, - "extended-ttl": {"required": False, "type": "int"} + "dns_retry_interval": {"required": False, "type": "int"}, + "extended_ttl": {"required": False, "type": "int"} } } @@ -231,15 +260,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_webfilter(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_webfilter(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_webfilter(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_webfilter_ips_urlfilter_setting.py b/lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_setting.py index d05b42141aa..aa4c9b21674 100644 --- a/lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_setting.py +++ b/lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_setting.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 @@ -27,12 +24,12 @@ ANSIBLE_METADATA = {'status': ['preview'], DOCUMENTATION = ''' --- module: fortios_webfilter_ips_urlfilter_setting -short_description: Configure IPS URL filter settings. +short_description: Configure IPS URL filter settings in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure webfilter feature and ips_urlfilter_setting 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 webfilter feature and ips_urlfilter_setting 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,46 +41,60 @@ 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 webfilter_ips_urlfilter_setting: description: - Configure IPS URL filter settings. default: null + type: dict suboptions: device: description: - Interface for this route. Source system.interface.name. + type: str distance: description: - Administrative distance (1 - 255) for this route. + type: int gateway: description: - Gateway IP address for this route. - geo-filter: + type: str + geo_filter: description: - Filter based on geographical location. Route will NOT be installed if the resolved IP address belongs to the country in the filter. + type: str ''' EXAMPLES = ''' @@ -93,6 +104,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPS URL filter settings. fortios_webfilter_ips_urlfilter_setting: @@ -100,11 +112,12 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" webfilter_ips_urlfilter_setting: device: " (source system.interface.name)" distance: "4" gateway: "" - geo-filter: "" + geo_filter: "" ''' RETURN = ''' @@ -127,7 +140,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 @@ -167,14 +180,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']: @@ -182,12 +197,12 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_webfilter_ips_urlfilter_setting_data(json): option_list = ['device', 'distance', 'gateway', - 'geo-filter'] + 'geo_filter'] dictionary = {} for attribute in option_list: @@ -197,44 +212,60 @@ def filter_webfilter_ips_urlfilter_setting_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 webfilter_ips_urlfilter_setting(data, fos): vdom = data['vdom'] webfilter_ips_urlfilter_setting_data = data['webfilter_ips_urlfilter_setting'] - filtered_data = filter_webfilter_ips_urlfilter_setting_data( - webfilter_ips_urlfilter_setting_data) + filtered_data = underscore_to_hyphen(filter_webfilter_ips_urlfilter_setting_data(webfilter_ips_urlfilter_setting_data)) + return fos.set('webfilter', 'ips-urlfilter-setting', 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_webfilter(data, fos): - login(data) - methodlist = ['webfilter_ips_urlfilter_setting'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['webfilter_ips_urlfilter_setting']: + resp = webfilter_ips_urlfilter_setting(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}, "webfilter_ips_urlfilter_setting": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { "device": {"required": False, "type": "str"}, "distance": {"required": False, "type": "int"}, "gateway": {"required": False, "type": "str"}, - "geo-filter": {"required": False, "type": "str"} + "geo_filter": {"required": False, "type": "str"} } } @@ -242,15 +273,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_webfilter(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_webfilter(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_webfilter(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_webfilter_ips_urlfilter_setting6.py b/lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_setting6.py index 33344031347..102b583dcc3 100644 --- a/lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_setting6.py +++ b/lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_setting6.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 @@ -27,12 +24,12 @@ ANSIBLE_METADATA = {'status': ['preview'], DOCUMENTATION = ''' --- module: fortios_webfilter_ips_urlfilter_setting6 -short_description: Configure IPS URL filter settings for IPv6. +short_description: Configure IPS URL filter settings 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 webfilter feature and ips_urlfilter_setting6 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 webfilter feature and ips_urlfilter_setting6 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,46 +41,60 @@ 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 webfilter_ips_urlfilter_setting6: description: - Configure IPS URL filter settings for IPv6. default: null + type: dict suboptions: device: description: - Interface for this route. Source system.interface.name. + type: str distance: description: - Administrative distance (1 - 255) for this route. + type: int gateway6: description: - Gateway IPv6 address for this route. - geo-filter: + type: str + geo_filter: description: - Filter based on geographical location. Route will NOT be installed if the resolved IPv6 address belongs to the country in the filter. + type: str ''' EXAMPLES = ''' @@ -93,6 +104,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPS URL filter settings for IPv6. fortios_webfilter_ips_urlfilter_setting6: @@ -100,11 +112,12 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" webfilter_ips_urlfilter_setting6: device: " (source system.interface.name)" distance: "4" gateway6: "" - geo-filter: "" + geo_filter: "" ''' RETURN = ''' @@ -127,7 +140,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 @@ -167,14 +180,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']: @@ -182,12 +197,12 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_webfilter_ips_urlfilter_setting6_data(json): option_list = ['device', 'distance', 'gateway6', - 'geo-filter'] + 'geo_filter'] dictionary = {} for attribute in option_list: @@ -197,44 +212,60 @@ def filter_webfilter_ips_urlfilter_setting6_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 webfilter_ips_urlfilter_setting6(data, fos): vdom = data['vdom'] webfilter_ips_urlfilter_setting6_data = data['webfilter_ips_urlfilter_setting6'] - filtered_data = filter_webfilter_ips_urlfilter_setting6_data( - webfilter_ips_urlfilter_setting6_data) + filtered_data = underscore_to_hyphen(filter_webfilter_ips_urlfilter_setting6_data(webfilter_ips_urlfilter_setting6_data)) + return fos.set('webfilter', 'ips-urlfilter-setting6', 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_webfilter(data, fos): - login(data) - methodlist = ['webfilter_ips_urlfilter_setting6'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['webfilter_ips_urlfilter_setting6']: + resp = webfilter_ips_urlfilter_setting6(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}, "webfilter_ips_urlfilter_setting6": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { "device": {"required": False, "type": "str"}, "distance": {"required": False, "type": "int"}, "gateway6": {"required": False, "type": "str"}, - "geo-filter": {"required": False, "type": "str"} + "geo_filter": {"required": False, "type": "str"} } } @@ -242,15 +273,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_webfilter(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_webfilter(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_webfilter(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_webfilter_override.py b/lib/ansible/modules/network/fortios/fortios_webfilter_override.py index fa962b12926..42233c95f9d 100644 --- a/lib/ansible/modules/network/fortios/fortios_webfilter_override.py +++ b/lib/ansible/modules/network/fortios/fortios_webfilter_override.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 @@ -27,12 +24,12 @@ ANSIBLE_METADATA = {'status': ['preview'], DOCUMENTATION = ''' --- module: fortios_webfilter_override -short_description: Configure FortiGuard Web Filter administrative overrides. +short_description: Configure FortiGuard Web Filter administrative overrides in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure webfilter feature and override 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 webfilter feature and override 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,65 +41,86 @@ 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 webfilter_override: description: - Configure FortiGuard Web Filter administrative overrides. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent expires: description: - "Override expiration date and time, from 5 minutes to 365 from now (format: yyyy/mm/dd hh:mm:ss)." + type: str id: description: - Override rule ID. required: true + type: int initiator: description: - Initiating user of override (read-only setting). + type: str ip: description: - IPv4 address which the override applies. + type: str ip6: description: - IPv6 address which the override applies. - new-profile: + type: str + new_profile: description: - Name of the new web filter profile used by the override. Source webfilter.profile.name. - old-profile: + type: str + old_profile: description: - Name of the web filter profile which the override applies. Source webfilter.profile.name. + type: str scope: description: - Override either the specific user, user group, IPv4 address, or IPv6 address. + type: str choices: - user - user-group @@ -111,15 +129,18 @@ options: status: description: - Enable/disable override rule. + type: str choices: - enable - disable user: description: - Name of the user which the override applies. - user-group: + type: str + user_group: description: - Specify the user group for which the override applies. Source user.group.name. + type: str ''' EXAMPLES = ''' @@ -129,6 +150,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure FortiGuard Web Filter administrative overrides. fortios_webfilter_override: @@ -136,19 +158,20 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" + state: "present" webfilter_override: - state: "present" expires: "" id: "4" initiator: "" ip: "" ip6: "" - new-profile: " (source webfilter.profile.name)" - old-profile: " (source webfilter.profile.name)" + new_profile: " (source webfilter.profile.name)" + old_profile: " (source webfilter.profile.name)" scope: "user" status: "enable" user: "" - user-group: " (source user.group.name)" + user_group: " (source user.group.name)" ''' RETURN = ''' @@ -171,7 +194,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 @@ -211,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']: @@ -226,14 +251,14 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_webfilter_override_data(json): option_list = ['expires', 'id', 'initiator', - 'ip', 'ip6', 'new-profile', - 'old-profile', 'scope', 'status', - 'user', 'user-group'] + 'ip', 'ip6', 'new_profile', + 'old_profile', 'scope', 'status', + 'user', 'user_group'] dictionary = {} for attribute in option_list: @@ -243,62 +268,80 @@ def filter_webfilter_override_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 webfilter_override(data, fos): vdom = data['vdom'] + state = data['state'] webfilter_override_data = data['webfilter_override'] - filtered_data = filter_webfilter_override_data(webfilter_override_data) - if webfilter_override_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_webfilter_override_data(webfilter_override_data)) + + if state == "present": return fos.set('webfilter', 'override', data=filtered_data, vdom=vdom) - elif webfilter_override_data['state'] == "absent": + elif state == "absent": return fos.delete('webfilter', 'override', 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_webfilter(data, fos): - login(data) - methodlist = ['webfilter_override'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['webfilter_override']: + resp = webfilter_override(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"]}, "webfilter_override": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "expires": {"required": False, "type": "str"}, "id": {"required": True, "type": "int"}, "initiator": {"required": False, "type": "str"}, "ip": {"required": False, "type": "str"}, "ip6": {"required": False, "type": "str"}, - "new-profile": {"required": False, "type": "str"}, - "old-profile": {"required": False, "type": "str"}, + "new_profile": {"required": False, "type": "str"}, + "old_profile": {"required": False, "type": "str"}, "scope": {"required": False, "type": "str", "choices": ["user", "user-group", "ip", "ip6"]}, "status": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "user": {"required": False, "type": "str"}, - "user-group": {"required": False, "type": "str"} + "user_group": {"required": False, "type": "str"} } } @@ -306,15 +349,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_webfilter(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_webfilter(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_webfilter(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_webfilter_profile.py b/lib/ansible/modules/network/fortios/fortios_webfilter_profile.py index 8fc7b6c1a71..7bb2df36c2e 100644 --- a/lib/ansible/modules/network/fortios/fortios_webfilter_profile.py +++ b/lib/ansible/modules/network/fortios/fortios_webfilter_profile.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 @@ -27,12 +24,12 @@ ANSIBLE_METADATA = {'status': ['preview'], DOCUMENTATION = ''' --- module: fortios_webfilter_profile -short_description: Configure Web filter profiles. +short_description: Configure Web filter profiles in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure webfilter 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 webfilter 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,113 +41,143 @@ 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 webfilter_profile: description: - Configure Web filter profiles. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent comment: description: - Optional comments. - extended-log: + type: str + extended_log: description: - Enable/disable extended logging for web filtering. + type: str choices: - enable - disable - ftgd-wf: + ftgd_wf: description: - FortiGuard Web Filter settings. + type: dict suboptions: - exempt-quota: + exempt_quota: description: - Do not stop quota for these categories. + type: str filters: description: - FortiGuard filters. + type: list suboptions: action: description: - Action to take for matches. + type: str choices: - block - authenticate - monitor - warning - auth-usr-grp: + auth_usr_grp: description: - Groups with permission to authenticate. + type: str suboptions: name: description: - User group name. Source user.group.name. required: true + type: str category: description: - Categories and groups the filter examines. + type: int id: description: - ID number. required: true + type: int log: description: - Enable/disable logging. + type: str choices: - enable - disable - override-replacemsg: + override_replacemsg: description: - Override replacement message. - warn-duration: + type: str + warn_duration: description: - Duration of warnings. - warning-duration-type: + type: str + warning_duration_type: description: - Re-display warning after closing browser or after a timeout. + type: str choices: - session - timeout - warning-prompt: + warning_prompt: description: - Warning prompts in each category or each domain. + type: str choices: - per-domain - per-category - max-quota-timeout: + max_quota_timeout: description: - Maximum FortiGuard quota used by single page view in seconds (excludes streams). + type: int options: description: - Options for FortiGuard Web Filter. + type: str choices: - error-allow - rate-server-ip @@ -159,32 +186,40 @@ options: ovrd: description: - Allow web filter profile overrides. + type: str quota: description: - FortiGuard traffic quota settings. + type: list suboptions: category: description: - FortiGuard categories to apply quota to (category action must be set to monitor). + type: str duration: description: - Duration of quota. + type: str id: description: - ID number. required: true - override-replacemsg: + type: int + override_replacemsg: description: - Override replacement message. + type: str type: description: - Quota type. + type: str choices: - time - traffic unit: description: - Traffic quota unit of measurement. + type: str choices: - B - KB @@ -193,45 +228,53 @@ options: value: description: - Traffic quota value. - rate-crl-urls: + type: int + rate_crl_urls: description: - Enable/disable rating CRL by URL. + type: str choices: - disable - enable - rate-css-urls: + rate_css_urls: description: - Enable/disable rating CSS by URL. + type: str choices: - disable - enable - rate-image-urls: + rate_image_urls: description: - Enable/disable rating images by URL. + type: str choices: - disable - enable - rate-javascript-urls: + rate_javascript_urls: description: - Enable/disable rating JavaScript by URL. + type: str choices: - disable - enable - https-replacemsg: + https_replacemsg: description: - Enable replacement messages for HTTPS. + type: str choices: - enable - disable - inspection-mode: + inspection_mode: description: - Web filtering inspection mode. + type: str choices: - proxy - flow-based - log-all-url: + log_all_url: description: - Enable/disable logging all URLs visited. + type: str choices: - enable - disable @@ -239,9 +282,11 @@ options: description: - Profile name. required: true + type: str options: description: - Options. + type: str choices: - activexfilter - cookiefilter @@ -258,50 +303,60 @@ options: override: description: - Web Filter override settings. + type: dict suboptions: - ovrd-cookie: + ovrd_cookie: description: - Allow/deny browser-based (cookie) overrides. + type: str choices: - allow - deny - ovrd-dur: + ovrd_dur: description: - Override duration. - ovrd-dur-mode: + type: str + ovrd_dur_mode: description: - Override duration mode. + type: str choices: - constant - ask - ovrd-scope: + ovrd_scope: description: - Override scope. + type: str choices: - user - user-group - ip - browser - ask - ovrd-user-group: + ovrd_user_group: description: - User groups with permission to use the override. + type: str suboptions: name: description: - User group name. Source user.group.name. required: true + type: str profile: description: - Web filter profile with permission to create overrides. + type: list suboptions: name: description: - Web profile. Source webfilter.profile.name. required: true - profile-attribute: + type: str + profile_attribute: description: - Profile attribute to retrieve from the RADIUS server. + type: str choices: - User-Name - NAS-IP-Address @@ -325,74 +380,89 @@ options: - Framed-AppleTalk-Zone - Acct-Session-Id - Acct-Multi-Session-Id - profile-type: + profile_type: description: - Override profile type. + type: str choices: - list - radius - ovrd-perm: + ovrd_perm: description: - Permitted override types. + type: str choices: - bannedword-override - urlfilter-override - fortiguard-wf-override - contenttype-check-override - post-action: + post_action: description: - Action taken for HTTP POST traffic. + type: str choices: - normal - block - replacemsg-group: + replacemsg_group: description: - Replacement message group. Source system.replacemsg-group.name. + type: str web: description: - Web content filtering settings. + type: dict suboptions: blacklist: description: - Enable/disable automatic addition of URLs detected by FortiSandbox to blacklist. + type: str choices: - enable - disable - bword-table: + bword_table: description: - Banned word table ID. Source webfilter.content.id. - bword-threshold: + type: int + bword_threshold: description: - Banned word score threshold. - content-header-list: + type: int + content_header_list: description: - Content header list. Source webfilter.content-header.id. - keyword-match: + type: int + keyword_match: description: - Search keywords to log when match is found. + type: str suboptions: pattern: description: - Pattern/keyword to search for. required: true - log-search: + type: str + log_search: description: - Enable/disable logging all search phrases. + type: str choices: - enable - disable - safe-search: + safe_search: description: - Safe search type. + type: str choices: - url - header - urlfilter-table: + urlfilter_table: description: - URL filter table ID. Source webfilter.urlfilter.id. + type: int whitelist: description: - FortiGuard whitelist settings. + type: str choices: - exempt-av - exempt-webcontent @@ -400,147 +470,173 @@ options: - exempt-dlp - exempt-rangeblock - extended-log-others - youtube-restrict: + youtube_restrict: description: - YouTube EDU filter level. + type: str choices: - none - strict - moderate - web-content-log: + web_content_log: description: - Enable/disable logging logging blocked web content. + type: str choices: - enable - disable - web-extended-all-action-log: + web_extended_all_action_log: description: - Enable/disable extended any filter action logging for web filtering. + type: str choices: - enable - disable - web-filter-activex-log: + web_filter_activex_log: description: - Enable/disable logging ActiveX. + type: str choices: - enable - disable - web-filter-applet-log: + web_filter_applet_log: description: - Enable/disable logging Java applets. + type: str choices: - enable - disable - web-filter-command-block-log: + web_filter_command_block_log: description: - Enable/disable logging blocked commands. + type: str choices: - enable - disable - web-filter-cookie-log: + web_filter_cookie_log: description: - Enable/disable logging cookie filtering. + type: str choices: - enable - disable - web-filter-cookie-removal-log: + web_filter_cookie_removal_log: description: - Enable/disable logging blocked cookies. + type: str choices: - enable - disable - web-filter-js-log: + web_filter_js_log: description: - Enable/disable logging Java scripts. + type: str choices: - enable - disable - web-filter-jscript-log: + web_filter_jscript_log: description: - Enable/disable logging JScripts. + type: str choices: - enable - disable - web-filter-referer-log: + web_filter_referer_log: description: - Enable/disable logging referrers. + type: str choices: - enable - disable - web-filter-unknown-log: + web_filter_unknown_log: description: - Enable/disable logging unknown scripts. + type: str choices: - enable - disable - web-filter-vbs-log: + web_filter_vbs_log: description: - Enable/disable logging VBS scripts. + type: str choices: - enable - disable - web-ftgd-err-log: + web_ftgd_err_log: description: - Enable/disable logging rating errors. + type: str choices: - enable - disable - web-ftgd-quota-usage: + web_ftgd_quota_usage: description: - Enable/disable logging daily quota usage. + type: str choices: - enable - disable - web-invalid-domain-log: + web_invalid_domain_log: description: - Enable/disable logging invalid domain names. + type: str choices: - enable - disable - web-url-log: + web_url_log: description: - Enable/disable logging URL filtering. + type: str choices: - enable - disable wisp: description: - Enable/disable web proxy WISP. + type: str choices: - enable - disable - wisp-algorithm: + wisp_algorithm: description: - WISP server selection algorithm. + type: str choices: - primary-secondary - round-robin - auto-learning - wisp-servers: + wisp_servers: description: - WISP servers. + type: list suboptions: name: description: - Server name. Source web-proxy.wisp.name. required: true - youtube-channel-filter: + type: str + youtube_channel_filter: description: - YouTube channel filter. + type: list suboptions: - channel-id: + channel_id: description: - YouTube channel ID to be filtered. + type: str comment: description: - Comment. + type: str id: description: - ID. required: true - youtube-channel-status: + type: int + youtube_channel_status: description: - YouTube channel filter status. + type: str choices: - disable - blacklist @@ -554,6 +650,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure Web filter profiles. fortios_webfilter_profile: @@ -561,26 +658,27 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" + state: "present" webfilter_profile: - state: "present" comment: "Optional comments." - extended-log: "enable" - ftgd-wf: - exempt-quota: "" + extended_log: "enable" + ftgd_wf: + exempt_quota: "" filters: - action: "block" - auth-usr-grp: + auth_usr_grp: - name: "default_name_10 (source user.group.name)" category: "11" id: "12" log: "enable" - override-replacemsg: "" - warn-duration: "" - warning-duration-type: "session" - warning-prompt: "per-domain" - max-quota-timeout: "18" + override_replacemsg: "" + warn_duration: "" + warning_duration_type: "session" + warning_prompt: "per-domain" + max_quota_timeout: "18" options: "error-allow" ovrd: "" quota: @@ -588,75 +686,75 @@ EXAMPLES = ''' category: "" duration: "" id: "24" - override-replacemsg: "" + override_replacemsg: "" type: "time" unit: "B" value: "28" - rate-crl-urls: "disable" - rate-css-urls: "disable" - rate-image-urls: "disable" - rate-javascript-urls: "disable" - https-replacemsg: "enable" - inspection-mode: "proxy" - log-all-url: "enable" + rate_crl_urls: "disable" + rate_css_urls: "disable" + rate_image_urls: "disable" + rate_javascript_urls: "disable" + https_replacemsg: "enable" + inspection_mode: "proxy" + log_all_url: "enable" name: "default_name_36" options: "activexfilter" override: - ovrd-cookie: "allow" - ovrd-dur: "" - ovrd-dur-mode: "constant" - ovrd-scope: "user" - ovrd-user-group: + ovrd_cookie: "allow" + ovrd_dur: "" + ovrd_dur_mode: "constant" + ovrd_scope: "user" + ovrd_user_group: - name: "default_name_44 (source user.group.name)" profile: - name: "default_name_46 (source webfilter.profile.name)" - profile-attribute: "User-Name" - profile-type: "list" - ovrd-perm: "bannedword-override" - post-action: "normal" - replacemsg-group: " (source system.replacemsg-group.name)" + profile_attribute: "User-Name" + profile_type: "list" + ovrd_perm: "bannedword-override" + post_action: "normal" + replacemsg_group: " (source system.replacemsg-group.name)" web: blacklist: "enable" - bword-table: "54 (source webfilter.content.id)" - bword-threshold: "55" - content-header-list: "56 (source webfilter.content-header.id)" - keyword-match: + bword_table: "54 (source webfilter.content.id)" + bword_threshold: "55" + content_header_list: "56 (source webfilter.content-header.id)" + keyword_match: - pattern: "" - log-search: "enable" - safe-search: "url" - urlfilter-table: "61 (source webfilter.urlfilter.id)" + log_search: "enable" + safe_search: "url" + urlfilter_table: "61 (source webfilter.urlfilter.id)" whitelist: "exempt-av" - youtube-restrict: "none" - web-content-log: "enable" - web-extended-all-action-log: "enable" - web-filter-activex-log: "enable" - web-filter-applet-log: "enable" - web-filter-command-block-log: "enable" - web-filter-cookie-log: "enable" - web-filter-cookie-removal-log: "enable" - web-filter-js-log: "enable" - web-filter-jscript-log: "enable" - web-filter-referer-log: "enable" - web-filter-unknown-log: "enable" - web-filter-vbs-log: "enable" - web-ftgd-err-log: "enable" - web-ftgd-quota-usage: "enable" - web-invalid-domain-log: "enable" - web-url-log: "enable" + youtube_restrict: "none" + web_content_log: "enable" + web_extended_all_action_log: "enable" + web_filter_activex_log: "enable" + web_filter_applet_log: "enable" + web_filter_command_block_log: "enable" + web_filter_cookie_log: "enable" + web_filter_cookie_removal_log: "enable" + web_filter_js_log: "enable" + web_filter_jscript_log: "enable" + web_filter_referer_log: "enable" + web_filter_unknown_log: "enable" + web_filter_vbs_log: "enable" + web_ftgd_err_log: "enable" + web_ftgd_quota_usage: "enable" + web_invalid_domain_log: "enable" + web_url_log: "enable" wisp: "enable" - wisp-algorithm: "primary-secondary" - wisp-servers: + wisp_algorithm: "primary-secondary" + wisp_servers: - name: "default_name_83 (source web-proxy.wisp.name)" - youtube-channel-filter: + youtube_channel_filter: - - channel-id: "" + channel_id: "" comment: "Comment." id: "87" - youtube-channel-status: "disable" + youtube_channel_status: "disable" ''' RETURN = ''' @@ -679,7 +777,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 @@ -719,14 +817,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']: @@ -734,85 +834,103 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_webfilter_profile_data(json): - option_list = ['comment', 'extended-log', 'ftgd-wf', - 'https-replacemsg', 'inspection-mode', 'log-all-url', + option_list = ['comment', 'extended_log', 'ftgd_wf', + 'https_replacemsg', 'inspection_mode', 'log_all_url', 'name', 'options', 'override', - 'ovrd-perm', 'post-action', 'replacemsg-group', - 'web', 'web-content-log', 'web-extended-all-action-log', - 'web-filter-activex-log', 'web-filter-applet-log', 'web-filter-command-block-log', - 'web-filter-cookie-log', 'web-filter-cookie-removal-log', 'web-filter-js-log', - 'web-filter-jscript-log', 'web-filter-referer-log', 'web-filter-unknown-log', - 'web-filter-vbs-log', 'web-ftgd-err-log', 'web-ftgd-quota-usage', - 'web-invalid-domain-log', 'web-url-log', 'wisp', - 'wisp-algorithm', 'wisp-servers', 'youtube-channel-filter', - 'youtube-channel-status'] + 'ovrd_perm', 'post_action', 'replacemsg_group', + 'web', 'web_content_log', 'web_extended_all_action_log', + 'web_filter_activex_log', 'web_filter_applet_log', 'web_filter_command_block_log', + 'web_filter_cookie_log', 'web_filter_cookie_removal_log', 'web_filter_js_log', + 'web_filter_jscript_log', 'web_filter_referer_log', 'web_filter_unknown_log', + 'web_filter_vbs_log', 'web_ftgd_err_log', 'web_ftgd_quota_usage', + 'web_invalid_domain_log', 'web_url_log', 'wisp', + 'wisp_algorithm', 'wisp_servers', 'youtube_channel_filter', + 'youtube_channel_status'] dictionary = {} for attribute in option_list: - if attribute in json: + if attribute in json and json[attribute] is not None: dictionary[attribute] = json[attribute] 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 webfilter_profile(data, fos): vdom = data['vdom'] + state = data['state'] webfilter_profile_data = data['webfilter_profile'] - filtered_data = filter_webfilter_profile_data(webfilter_profile_data) - if webfilter_profile_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_webfilter_profile_data(webfilter_profile_data)) + + if state == "present": return fos.set('webfilter', 'profile', data=filtered_data, vdom=vdom) - elif webfilter_profile_data['state'] == "absent": + elif state == "absent": return fos.delete('webfilter', '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_webfilter(data, fos): - login(data) - methodlist = ['webfilter_profile'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['webfilter_profile']: + resp = webfilter_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": "False"}, + "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "webfilter_profile": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "comment": {"required": False, "type": "str"}, - "extended-log": {"required": False, "type": "str", + "extended_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ftgd-wf": {"required": False, "type": "dict", + "ftgd_wf": {"required": False, "type": "dict", "options": { - "exempt-quota": {"required": False, "type": "str"}, + "exempt_quota": {"required": False, "type": "str"}, "filters": {"required": False, "type": "list", "options": { "action": {"required": False, "type": "str", "choices": ["block", "authenticate", "monitor", "warning"]}, - "auth-usr-grp": {"required": False, "type": "str", + "auth_usr_grp": {"required": False, "type": "str", "options": { "name": {"required": True, "type": "str"} }}, @@ -820,14 +938,14 @@ def main(): "id": {"required": True, "type": "int"}, "log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-replacemsg": {"required": False, "type": "str"}, - "warn-duration": {"required": False, "type": "str"}, - "warning-duration-type": {"required": False, "type": "str", + "override_replacemsg": {"required": False, "type": "str"}, + "warn_duration": {"required": False, "type": "str"}, + "warning_duration_type": {"required": False, "type": "str", "choices": ["session", "timeout"]}, - "warning-prompt": {"required": False, "type": "str", + "warning_prompt": {"required": False, "type": "str", "choices": ["per-domain", "per-category"]} }}, - "max-quota-timeout": {"required": False, "type": "int"}, + "max_quota_timeout": {"required": False, "type": "int"}, "options": {"required": False, "type": "str", "choices": ["error-allow", "rate-server-ip", "connect-request-bypass", "ftgd-disable"]}, @@ -837,7 +955,7 @@ def main(): "category": {"required": False, "type": "str"}, "duration": {"required": False, "type": "str"}, "id": {"required": True, "type": "int"}, - "override-replacemsg": {"required": False, "type": "str"}, + "override_replacemsg": {"required": False, "type": "str"}, "type": {"required": False, "type": "str", "choices": ["time", "traffic"]}, "unit": {"required": False, "type": "str", @@ -845,20 +963,20 @@ def main(): "GB"]}, "value": {"required": False, "type": "int"} }}, - "rate-crl-urls": {"required": False, "type": "str", + "rate_crl_urls": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "rate-css-urls": {"required": False, "type": "str", + "rate_css_urls": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "rate-image-urls": {"required": False, "type": "str", + "rate_image_urls": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "rate-javascript-urls": {"required": False, "type": "str", + "rate_javascript_urls": {"required": False, "type": "str", "choices": ["disable", "enable"]} }}, - "https-replacemsg": {"required": False, "type": "str", + "https_replacemsg": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "inspection-mode": {"required": False, "type": "str", + "inspection_mode": {"required": False, "type": "str", "choices": ["proxy", "flow-based"]}, - "log-all-url": {"required": False, "type": "str", + "log_all_url": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "name": {"required": True, "type": "str"}, "options": {"required": False, "type": "str", @@ -868,15 +986,15 @@ def main(): "wf-referer", "wf-cookie", "per-user-bwl"]}, "override": {"required": False, "type": "dict", "options": { - "ovrd-cookie": {"required": False, "type": "str", + "ovrd_cookie": {"required": False, "type": "str", "choices": ["allow", "deny"]}, - "ovrd-dur": {"required": False, "type": "str"}, - "ovrd-dur-mode": {"required": False, "type": "str", + "ovrd_dur": {"required": False, "type": "str"}, + "ovrd_dur_mode": {"required": False, "type": "str", "choices": ["constant", "ask"]}, - "ovrd-scope": {"required": False, "type": "str", + "ovrd_scope": {"required": False, "type": "str", "choices": ["user", "user-group", "ip", "browser", "ask"]}, - "ovrd-user-group": {"required": False, "type": "str", + "ovrd_user_group": {"required": False, "type": "str", "options": { "name": {"required": True, "type": "str"} }}, @@ -884,7 +1002,7 @@ def main(): "options": { "name": {"required": True, "type": "str"} }}, - "profile-attribute": {"required": False, "type": "str", + "profile_attribute": {"required": False, "type": "str", "choices": ["User-Name", "NAS-IP-Address", "Framed-IP-Address", "Framed-IP-Netmask", "Filter-Id", "Login-IP-Host", "Reply-Message", "Callback-Number", "Callback-Id", @@ -893,84 +1011,84 @@ def main(): "Proxy-State", "Login-LAT-Service", "Login-LAT-Node", "Login-LAT-Group", "Framed-AppleTalk-Zone", "Acct-Session-Id", "Acct-Multi-Session-Id"]}, - "profile-type": {"required": False, "type": "str", + "profile_type": {"required": False, "type": "str", "choices": ["list", "radius"]} }}, - "ovrd-perm": {"required": False, "type": "str", + "ovrd_perm": {"required": False, "type": "str", "choices": ["bannedword-override", "urlfilter-override", "fortiguard-wf-override", "contenttype-check-override"]}, - "post-action": {"required": False, "type": "str", + "post_action": {"required": False, "type": "str", "choices": ["normal", "block"]}, - "replacemsg-group": {"required": False, "type": "str"}, + "replacemsg_group": {"required": False, "type": "str"}, "web": {"required": False, "type": "dict", "options": { "blacklist": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "bword-table": {"required": False, "type": "int"}, - "bword-threshold": {"required": False, "type": "int"}, - "content-header-list": {"required": False, "type": "int"}, - "keyword-match": {"required": False, "type": "str", + "bword_table": {"required": False, "type": "int"}, + "bword_threshold": {"required": False, "type": "int"}, + "content_header_list": {"required": False, "type": "int"}, + "keyword_match": {"required": False, "type": "str", "options": { "pattern": {"required": True, "type": "str"} }}, - "log-search": {"required": False, "type": "str", + "log_search": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "safe-search": {"required": False, "type": "str", + "safe_search": {"required": False, "type": "str", "choices": ["url", "header"]}, - "urlfilter-table": {"required": False, "type": "int"}, + "urlfilter_table": {"required": False, "type": "int"}, "whitelist": {"required": False, "type": "str", "choices": ["exempt-av", "exempt-webcontent", "exempt-activex-java-cookie", "exempt-dlp", "exempt-rangeblock", "extended-log-others"]}, - "youtube-restrict": {"required": False, "type": "str", + "youtube_restrict": {"required": False, "type": "str", "choices": ["none", "strict", "moderate"]} }}, - "web-content-log": {"required": False, "type": "str", + "web_content_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-extended-all-action-log": {"required": False, "type": "str", + "web_extended_all_action_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-filter-activex-log": {"required": False, "type": "str", + "web_filter_activex_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-filter-applet-log": {"required": False, "type": "str", + "web_filter_applet_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-filter-command-block-log": {"required": False, "type": "str", + "web_filter_command_block_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-filter-cookie-log": {"required": False, "type": "str", + "web_filter_cookie_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-filter-cookie-removal-log": {"required": False, "type": "str", + "web_filter_cookie_removal_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-filter-js-log": {"required": False, "type": "str", + "web_filter_js_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-filter-jscript-log": {"required": False, "type": "str", + "web_filter_jscript_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-filter-referer-log": {"required": False, "type": "str", + "web_filter_referer_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-filter-unknown-log": {"required": False, "type": "str", + "web_filter_unknown_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-filter-vbs-log": {"required": False, "type": "str", + "web_filter_vbs_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-ftgd-err-log": {"required": False, "type": "str", + "web_ftgd_err_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-ftgd-quota-usage": {"required": False, "type": "str", + "web_ftgd_quota_usage": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-invalid-domain-log": {"required": False, "type": "str", + "web_invalid_domain_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "web-url-log": {"required": False, "type": "str", + "web_url_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "wisp": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "wisp-algorithm": {"required": False, "type": "str", + "wisp_algorithm": {"required": False, "type": "str", "choices": ["primary-secondary", "round-robin", "auto-learning"]}, - "wisp-servers": {"required": False, "type": "list", + "wisp_servers": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, - "youtube-channel-filter": {"required": False, "type": "list", + "youtube_channel_filter": {"required": False, "type": "list", "options": { - "channel-id": {"required": False, "type": "str"}, + "channel_id": {"required": False, "type": "str"}, "comment": {"required": False, "type": "str"}, "id": {"required": True, "type": "int"} }}, - "youtube-channel-status": {"required": False, "type": "str", + "youtube_channel_status": {"required": False, "type": "str", "choices": ["disable", "blacklist", "whitelist"]} } @@ -979,15 +1097,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_webfilter(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_webfilter(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_webfilter(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_webfilter_search_engine.py b/lib/ansible/modules/network/fortios/fortios_webfilter_search_engine.py index ca0b680b55d..0af1b014227 100644 --- a/lib/ansible/modules/network/fortios/fortios_webfilter_search_engine.py +++ b/lib/ansible/modules/network/fortios/fortios_webfilter_search_engine.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 @@ -27,12 +24,12 @@ ANSIBLE_METADATA = {'status': ['preview'], DOCUMENTATION = ''' --- module: fortios_webfilter_search_engine -short_description: Configure web filter search engines. +short_description: Configure web filter search engines in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure webfilter feature and search_engine 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 webfilter feature and search_engine 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,69 +41,89 @@ 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 webfilter_search_engine: description: - Configure web filter search engines. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent charset: description: - Search engine charset. + type: str choices: - utf-8 - gb2312 hostname: description: - Hostname (regular expression). + type: str name: description: - Search engine name. required: true + type: str query: description: - Code used to prefix a query (must end with an equals character). + type: str safesearch: description: - Safe search method. You can disable safe search, add the safe search string to URLs, or insert a safe search header. + type: str choices: - disable - url - header - safesearch-str: + safesearch_str: description: - Safe search parameter used in the URL. + type: str url: description: - URL (regular expression). + type: str ''' EXAMPLES = ''' @@ -116,22 +133,24 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure web filter search engines. fortios_webfilter_search_engine: - host: "{{ host }}" + host: "{{ host }}" username: "{{ username }}" password: "{{ password }}" - vdom: "{{ vdom }}" + vdom: "{{ vdom }}" + https: "False" + state: "present" webfilter_search_engine: - state: "present" charset: "utf-8" hostname: "myhostname" name: "default_name_5" query: "" safesearch: "disable" - safesearch-str: "" - url: "http://myurl.com" + safesearch_str: "" + url: "myurl.com" ''' RETURN = ''' @@ -154,7 +173,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 @@ -194,14 +213,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,65 +230,82 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_webfilter_search_engine_data(json): option_list = ['charset', 'hostname', 'name', - 'query', 'safesearch', 'safesearch-str', + 'query', 'safesearch', 'safesearch_str', 'url'] dictionary = {} for attribute in option_list: - if attribute in json: + if attribute in json and json[attribute] is not None: dictionary[attribute] = json[attribute] 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 webfilter_search_engine(data, fos): vdom = data['vdom'] + state = data['state'] webfilter_search_engine_data = data['webfilter_search_engine'] - filtered_data = filter_webfilter_search_engine_data( - webfilter_search_engine_data) - if webfilter_search_engine_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_webfilter_search_engine_data(webfilter_search_engine_data)) + + if state == "present": return fos.set('webfilter', 'search-engine', data=filtered_data, vdom=vdom) - elif webfilter_search_engine_data['state'] == "absent": + elif state == "absent": return fos.delete('webfilter', 'search-engine', 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_webfilter(data, fos): - login(data) - methodlist = ['webfilter_search_engine'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['webfilter_search_engine']: + resp = webfilter_search_engine(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"]}, "webfilter_search_engine": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "charset": {"required": False, "type": "str", "choices": ["utf-8", "gb2312"]}, "hostname": {"required": False, "type": "str"}, @@ -275,7 +313,7 @@ def main(): "query": {"required": False, "type": "str"}, "safesearch": {"required": False, "type": "str", "choices": ["disable", "url", "header"]}, - "safesearch-str": {"required": False, "type": "str"}, + "safesearch_str": {"required": False, "type": "str"}, "url": {"required": False, "type": "str"} } @@ -284,15 +322,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_webfilter(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_webfilter(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_webfilter(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_webfilter_urlfilter.py b/lib/ansible/modules/network/fortios/fortios_webfilter_urlfilter.py index 63d10a0c197..9f4c59a9ecc 100644 --- a/lib/ansible/modules/network/fortios/fortios_webfilter_urlfilter.py +++ b/lib/ansible/modules/network/fortios/fortios_webfilter_urlfilter.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_webfilter_urlfilter short_description: Configure URL filter lists in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by - allowing the user to configure webfilter feature and urlfilter 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 webfilter feature and urlfilter 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,58 +41,75 @@ 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 webfilter_urlfilter: description: - Configure URL filter lists. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent comment: description: - Optional comments. + type: str entries: description: - URL filter entries. + type: list suboptions: action: description: - Action to take for URL filter matches. + type: str choices: - exempt - block - allow - monitor - dns-address-family: + dns_address_family: description: - Resolve IPv4 address, IPv6 address, or both from DNS server. + type: str choices: - ipv4 - ipv6 @@ -103,6 +117,7 @@ options: exempt: description: - If action is set to exempt, select the security profile operations that exempt URLs skip. Separate multiple options with a space. + type: str choices: - av - web-content @@ -116,18 +131,22 @@ options: description: - Id. required: true - referrer-host: + type: int + referrer_host: description: - Referrer host name. + type: str status: description: - Enable/disable this URL filter. + type: str choices: - enable - disable type: description: - Filter type (simple, regex, or wildcard). + type: str choices: - simple - regex @@ -135,25 +154,31 @@ options: url: description: - URL to be filtered. - web-proxy-profile: + type: str + web_proxy_profile: description: - Web proxy profile. Source web-proxy.profile.name. + type: str id: description: - ID. required: true - ip-addr-block: + type: int + ip_addr_block: description: - Enable/disable blocking URLs when the hostname appears as an IP address. + type: str choices: - enable - disable name: description: - Name of URL filter list. - one-arm-ips-urlfilter: + type: str + one_arm_ips_urlfilter: description: - Enable/disable DNS resolver for one-arm IPS URL filter operation. + type: str choices: - enable - disable @@ -166,6 +191,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure URL filter lists. fortios_webfilter_urlfilter: @@ -173,24 +199,25 @@ EXAMPLES = ''' username: "{{ username }}" password: "{{ password }}" vdom: "{{ vdom }}" + https: "False" + state: "present" webfilter_urlfilter: - state: "present" comment: "Optional comments." entries: - action: "exempt" - dns-address-family: "ipv4" + dns_address_family: "ipv4" exempt: "av" id: "8" - referrer-host: "myhostname" + referrer_host: "myhostname" status: "enable" type: "simple" url: "myurl.com" - web-proxy-profile: " (source web-proxy.profile.name)" + web_proxy_profile: " (source web-proxy.profile.name)" id: "14" - ip-addr-block: "enable" + ip_addr_block: "enable" name: "default_name_16" - one-arm-ips-urlfilter: "enable" + one_arm_ips_urlfilter: "enable" ''' RETURN = ''' @@ -253,14 +280,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']: @@ -268,12 +297,12 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_webfilter_urlfilter_data(json): option_list = ['comment', 'entries', 'id', - 'ip-addr-block', 'name', 'one-arm-ips-urlfilter'] + 'ip_addr_block', 'name', 'one_arm_ips_urlfilter'] dictionary = {} for attribute in option_list: @@ -283,74 +312,92 @@ def filter_webfilter_urlfilter_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 webfilter_urlfilter(data, fos): vdom = data['vdom'] + state = data['state'] webfilter_urlfilter_data = data['webfilter_urlfilter'] - filtered_data = filter_webfilter_urlfilter_data(webfilter_urlfilter_data) - if webfilter_urlfilter_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_webfilter_urlfilter_data(webfilter_urlfilter_data)) + + if state == "present": return fos.set('webfilter', 'urlfilter', data=filtered_data, vdom=vdom) - elif webfilter_urlfilter_data['state'] == "absent": + elif state == "absent": return fos.delete('webfilter', 'urlfilter', 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_webfilter(data, fos): - login(data) - methodlist = ['webfilter_urlfilter'] - for method in methodlist: - if data[method]: - resp = eval(method)(data, fos) - break + if data['webfilter_urlfilter']: + resp = webfilter_urlfilter(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"]}, "webfilter_urlfilter": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "comment": {"required": False, "type": "str"}, "entries": {"required": False, "type": "list", "options": { "action": {"required": False, "type": "str", "choices": ["exempt", "block", "allow", "monitor"]}, - "dns-address-family": {"required": False, "type": "str", + "dns_address_family": {"required": False, "type": "str", "choices": ["ipv4", "ipv6", "both"]}, "exempt": {"required": False, "type": "str", "choices": ["av", "web-content", "activex-java-cookie", "dlp", "fortiguard", "range-block", "pass", "all"]}, "id": {"required": True, "type": "int"}, - "referrer-host": {"required": False, "type": "str"}, + "referrer_host": {"required": False, "type": "str"}, "status": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "type": {"required": False, "type": "str", "choices": ["simple", "regex", "wildcard"]}, "url": {"required": False, "type": "str"}, - "web-proxy-profile": {"required": False, "type": "str"} + "web_proxy_profile": {"required": False, "type": "str"} }}, "id": {"required": True, "type": "int"}, - "ip-addr-block": {"required": False, "type": "str", + "ip_addr_block": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "name": {"required": False, "type": "str"}, - "one-arm-ips-urlfilter": {"required": False, "type": "str", + "one_arm_ips_urlfilter": {"required": False, "type": "str", "choices": ["enable", "disable"]} } @@ -359,15 +406,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_webfilter(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_webfilter(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_webfilter(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_wireless_controller_global.py b/lib/ansible/modules/network/fortios/fortios_wireless_controller_global.py index 308b89af4d9..ec7d93b774d 100644 --- a/lib/ansible/modules/network/fortios/fortios_wireless_controller_global.py +++ b/lib/ansible/modules/network/fortios/fortios_wireless_controller_global.py @@ -26,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_wireless_controller_global short_description: Configure wireless controller global settings in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify wireless_controller feature and global category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -41,49 +41,63 @@ 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: true + version_added: 2.9 wireless_controller_global: description: - Configure wireless controller global settings. default: null + type: dict suboptions: - ap-log-server: + ap_log_server: description: - - Enable/disable configuring APs or FortiAPs to send log messages to a syslog server (default = disable). + - Enable/disable configuring APs or FortiAPs to send log messages to a syslog server . + type: str choices: - enable - disable - ap-log-server-ip: + ap_log_server_ip: description: - IP address that APs or FortiAPs send log messages to. - ap-log-server-port: + type: str + ap_log_server_port: description: - Port that APs or FortiAPs send log messages to. - control-message-offload: + type: int + control_message_offload: description: - Configure CAPWAP control message data channel offload. + type: str choices: - ebp-frame - aeroscout-tag @@ -92,54 +106,67 @@ options: - sta-cap-list - stats - aeroscout-mu - data-ethernet-II: + data_ethernet_II: description: - - Configure the wireless controller to use Ethernet II or 802.3 frames with 802.3 data tunnel mode (default = disable). + - Configure the wireless controller to use Ethernet II or 802.3 frames with 802.3 data tunnel mode . + type: str choices: - enable - disable - discovery-mc-addr: + discovery_mc_addr: description: - - Multicast IP address for AP discovery (default = 244.0.1.140). - fiapp-eth-type: + - Multicast IP address for AP discovery . + type: str + fiapp_eth_type: description: - - Ethernet type for Fortinet Inter-Access Point Protocol (IAPP), or IEEE 802.11f, packets (0 - 65535, default = 5252). - image-download: + - Ethernet type for Fortinet Inter-Access Point Protocol (IAPP), or IEEE 802.11f, packets (0 - 65535). + type: int + image_download: description: - Enable/disable WTP image download at join time. + type: str choices: - enable - disable - ipsec-base-ip: + ipsec_base_ip: description: - - Base IP address for IPsec VPN tunnels between the access points and the wireless controller (default = 169.254.0.1). - link-aggregation: + - Base IP address for IPsec VPN tunnels between the access points and the wireless controller . + type: str + link_aggregation: description: - - Enable/disable calculating the CAPWAP transmit hash to load balance sessions to link aggregation nodes (default = disable). + - Enable/disable calculating the CAPWAP transmit hash to load balance sessions to link aggregation nodes . + type: str choices: - enable - disable location: description: - Description of the location of the wireless controller. - max-clients: + type: str + max_clients: description: - - Maximum number of clients that can connect simultaneously (default = 0, meaning no limitation). - max-retransmit: + - Maximum number of clients that can connect simultaneously . + type: int + max_retransmit: description: - - Maximum number of tunnel packet retransmissions (0 - 64, default = 3). - mesh-eth-type: + - Maximum number of tunnel packet retransmissions (0 - 64). + type: int + mesh_eth_type: description: - - Mesh Ethernet identifier included in backhaul packets (0 - 65535, default = 8755). + - Mesh Ethernet identifier included in backhaul packets (0 - 65535). + type: int name: description: - Name of the wireless controller. - rogue-scan-mac-adjacency: + type: str + rogue_scan_mac_adjacency: description: - - Maximum numerical difference between an AP's Ethernet and wireless MAC values to match for rogue detection (0 - 31, default = 7). - wtp-share: + - Maximum numerical difference between an AP's Ethernet and wireless MAC values to match for rogue detection (0 - 31). + type: int + wtp_share: description: - Enable/disable sharing of WTPs between VDOMs. + type: str choices: - enable - disable @@ -152,6 +179,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure wireless controller global settings. fortios_wireless_controller_global: @@ -161,23 +189,23 @@ EXAMPLES = ''' vdom: "{{ vdom }}" https: "False" wireless_controller_global: - ap-log-server: "enable" - ap-log-server-ip: "" - ap-log-server-port: "5" - control-message-offload: "ebp-frame" - data-ethernet-II: "enable" - discovery-mc-addr: "" - fiapp-eth-type: "9" - image-download: "enable" - ipsec-base-ip: "" - link-aggregation: "enable" + ap_log_server: "enable" + ap_log_server_ip: "" + ap_log_server_port: "5" + control_message_offload: "ebp-frame" + data_ethernet_II: "enable" + discovery_mc_addr: "" + fiapp_eth_type: "9" + image_download: "enable" + ipsec_base_ip: "" + link_aggregation: "enable" location: "" - max-clients: "14" - max-retransmit: "15" - mesh-eth-type: "16" + max_clients: "14" + max_retransmit: "15" + mesh_eth_type: "16" name: "default_name_17" - rogue-scan-mac-adjacency: "18" - wtp-share: "enable" + rogue_scan_mac_adjacency: "18" + wtp_share: "enable" ''' RETURN = ''' @@ -240,12 +268,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 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']: @@ -253,16 +285,16 @@ def login(data, fos): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_wireless_controller_global_data(json): - option_list = ['ap-log-server', 'ap-log-server-ip', 'ap-log-server-port', - 'control-message-offload', 'data-ethernet-II', 'discovery-mc-addr', - 'fiapp-eth-type', 'image-download', 'ipsec-base-ip', - 'link-aggregation', 'location', 'max-clients', - 'max-retransmit', 'mesh-eth-type', 'name', - 'rogue-scan-mac-adjacency', 'wtp-share'] + option_list = ['ap_log_server', 'ap_log_server_ip', 'ap_log_server_port', + 'control_message_offload', 'data_ethernet_II', 'discovery_mc_addr', + 'fiapp_eth_type', 'image_download', 'ipsec_base_ip', + 'link_aggregation', 'location', 'max_clients', + 'max_retransmit', 'mesh_eth_type', 'name', + 'rogue_scan_mac_adjacency', 'wtp_share'] dictionary = {} for attribute in option_list: @@ -272,10 +304,23 @@ def filter_wireless_controller_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 wireless_controller_global(data, fos): vdom = data['vdom'] wireless_controller_global_data = data['wireless_controller_global'] - filtered_data = filter_wireless_controller_global_data(wireless_controller_global_data) + filtered_data = underscore_to_hyphen(filter_wireless_controller_global_data(wireless_controller_global_data)) return fos.set('wireless-controller', 'global', @@ -283,50 +328,56 @@ def wireless_controller_global(data, fos): vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_wireless_controller(data, fos): - login(data, fos) if data['wireless_controller_global']: resp = wireless_controller_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}, "wireless_controller_global": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "ap-log-server": {"required": False, "type": "str", + "ap_log_server": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ap-log-server-ip": {"required": False, "type": "str"}, - "ap-log-server-port": {"required": False, "type": "int"}, - "control-message-offload": {"required": False, "type": "str", + "ap_log_server_ip": {"required": False, "type": "str"}, + "ap_log_server_port": {"required": False, "type": "int"}, + "control_message_offload": {"required": False, "type": "str", "choices": ["ebp-frame", "aeroscout-tag", "ap-list", "sta-list", "sta-cap-list", "stats", "aeroscout-mu"]}, - "data-ethernet-II": {"required": False, "type": "str", + "data_ethernet_II": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "discovery-mc-addr": {"required": False, "type": "ipv4-address-multicast"}, - "fiapp-eth-type": {"required": False, "type": "int"}, - "image-download": {"required": False, "type": "str", + "discovery_mc_addr": {"required": False, "type": "str"}, + "fiapp_eth_type": {"required": False, "type": "int"}, + "image_download": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ipsec-base-ip": {"required": False, "type": "str"}, - "link-aggregation": {"required": False, "type": "str", + "ipsec_base_ip": {"required": False, "type": "str"}, + "link_aggregation": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "location": {"required": False, "type": "str"}, - "max-clients": {"required": False, "type": "int"}, - "max-retransmit": {"required": False, "type": "int"}, - "mesh-eth-type": {"required": False, "type": "int"}, + "max_clients": {"required": False, "type": "int"}, + "max_retransmit": {"required": False, "type": "int"}, + "mesh_eth_type": {"required": False, "type": "int"}, "name": {"required": False, "type": "str"}, - "rogue-scan-mac-adjacency": {"required": False, "type": "int"}, - "wtp-share": {"required": False, "type": "str", + "rogue_scan_mac_adjacency": {"required": False, "type": "int"}, + "wtp_share": {"required": False, "type": "str", "choices": ["enable", "disable"]} } @@ -335,14 +386,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") - 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_wireless_controller(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_wireless_controller(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_wireless_controller(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_wireless_controller_setting.py b/lib/ansible/modules/network/fortios/fortios_wireless_controller_setting.py index 6daa745f3c6..038746265c9 100644 --- a/lib/ansible/modules/network/fortios/fortios_wireless_controller_setting.py +++ b/lib/ansible/modules/network/fortios/fortios_wireless_controller_setting.py @@ -26,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_wireless_controller_setting short_description: VDOM wireless controller configuration in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify wireless_controller feature and setting category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -41,40 +41,52 @@ 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: true + version_added: 2.9 wireless_controller_setting: description: - VDOM wireless controller configuration. default: null + type: dict suboptions: - account-id: + account_id: description: - FortiCloud customer account ID. + type: str country: description: - Country or region in which the FortiGate is located. The country determines the 802.11 bands and channels that are available. + type: str choices: - NA - AL @@ -204,15 +216,17 @@ options: - ZW - JP - CA - duplicate-ssid: + duplicate_ssid: description: - Enable/disable allowing Virtual Access Points (VAPs) to use the same SSID name in the same VDOM. + type: str choices: - enable - disable - fapc-compatibility: + fapc_compatibility: description: - Enable/disable FAP-C series compatibility. + type: str choices: - enable - disable @@ -225,6 +239,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: VDOM wireless controller configuration. fortios_wireless_controller_setting: @@ -234,10 +249,10 @@ EXAMPLES = ''' vdom: "{{ vdom }}" https: "False" wireless_controller_setting: - account-id: "" + account_id: "" country: "NA" - duplicate-ssid: "enable" - fapc-compatibility: "enable" + duplicate_ssid: "enable" + fapc_compatibility: "enable" ''' RETURN = ''' @@ -300,12 +315,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 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']: @@ -313,12 +332,12 @@ def login(data, fos): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_wireless_controller_setting_data(json): - option_list = ['account-id', 'country', 'duplicate-ssid', - 'fapc-compatibility'] + option_list = ['account_id', 'country', 'duplicate_ssid', + 'fapc_compatibility'] dictionary = {} for attribute in option_list: @@ -328,17 +347,15 @@ def filter_wireless_controller_setting_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +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 @@ -346,35 +363,41 @@ def flatten_multilists_attributes(data): def wireless_controller_setting(data, fos): vdom = data['vdom'] wireless_controller_setting_data = data['wireless_controller_setting'] - flattened_data = flatten_multilists_attributes(wireless_controller_setting_data) - filtered_data = filter_wireless_controller_setting_data(flattened_data) + filtered_data = underscore_to_hyphen(filter_wireless_controller_setting_data(wireless_controller_setting_data)) + return fos.set('wireless-controller', 'setting', 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_wireless_controller(data, fos): - login(data, fos) if data['wireless_controller_setting']: resp = wireless_controller_setting(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}, "wireless_controller_setting": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "account-id": {"required": False, "type": "str"}, + "account_id": {"required": False, "type": "str"}, "country": {"required": False, "type": "str", "choices": ["NA", "AL", "DZ", "AO", "AR", "AM", @@ -419,9 +442,9 @@ def main(): "UZ", "VE", "VN", "YE", "ZB", "ZW", "JP", "CA"]}, - "duplicate-ssid": {"required": False, "type": "str", + "duplicate_ssid": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "fapc-compatibility": {"required": False, "type": "str", + "fapc_compatibility": {"required": False, "type": "str", "choices": ["enable", "disable"]} } @@ -430,14 +453,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") - 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_wireless_controller(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_wireless_controller(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_wireless_controller(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_wireless_controller_utm_profile.py b/lib/ansible/modules/network/fortios/fortios_wireless_controller_utm_profile.py index 5298c7f9563..aef19b7b8d6 100644 --- a/lib/ansible/modules/network/fortios/fortios_wireless_controller_utm_profile.py +++ b/lib/ansible/modules/network/fortios/fortios_wireless_controller_utm_profile.py @@ -26,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_wireless_controller_utm_profile short_description: Configure UTM (Unified Threat Management) profile in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify wireless_controller feature and utm_profile category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -41,72 +41,93 @@ 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: 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 wireless_controller_utm_profile: description: - Configure UTM (Unified Threat Management) profile. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent - antivirus-profile: + antivirus_profile: description: - AntiVirus profile name. Source antivirus.profile.name. - application-list: + type: str + application_list: description: - Application control list name. Source application.list.name. + type: str comment: description: - Comment. - ips-sensor: + type: str + ips_sensor: description: - IPS sensor name. Source ips.sensor.name. + type: str name: description: - UTM profile name. required: true - scan-botnet-connections: + type: str + scan_botnet_connections: description: - Block or monitor connections to Botnet servers or disable Botnet scanning. + type: str choices: - disable - - block - monitor - utm-log: + - block + utm_log: description: - Enable/disable UTM logging. + type: str choices: - enable - disable - webfilter-profile: + webfilter_profile: description: - WebFilter profile name. Source webfilter.profile.name. + type: str ''' EXAMPLES = ''' @@ -116,6 +137,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure UTM (Unified Threat Management) profile. fortios_wireless_controller_utm_profile: @@ -124,16 +146,16 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" wireless_controller_utm_profile: - state: "present" - antivirus-profile: " (source antivirus.profile.name)" - application-list: " (source application.list.name)" + antivirus_profile: " (source antivirus.profile.name)" + application_list: " (source application.list.name)" comment: "Comment." - ips-sensor: " (source ips.sensor.name)" + ips_sensor: " (source ips.sensor.name)" name: "default_name_7" - scan-botnet-connections: "disable" - utm-log: "enable" - webfilter-profile: " (source webfilter.profile.name)" + scan_botnet_connections: "disable" + utm_log: "enable" + webfilter_profile: " (source webfilter.profile.name)" ''' RETURN = ''' @@ -196,12 +218,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 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,13 +235,13 @@ def login(data, fos): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_wireless_controller_utm_profile_data(json): - option_list = ['antivirus-profile', 'application-list', 'comment', - 'ips-sensor', 'name', 'scan-botnet-connections', - 'utm-log', 'webfilter-profile'] + option_list = ['antivirus_profile', 'application_list', 'comment', + 'ips_sensor', 'name', 'scan_botnet_connections', + 'utm_log', 'webfilter_profile'] dictionary = {} for attribute in option_list: @@ -225,71 +251,76 @@ def filter_wireless_controller_utm_profile_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +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 wireless_controller_utm_profile(data, fos): vdom = data['vdom'] + state = data['state'] wireless_controller_utm_profile_data = data['wireless_controller_utm_profile'] - flattened_data = flatten_multilists_attributes(wireless_controller_utm_profile_data) - filtered_data = filter_wireless_controller_utm_profile_data(flattened_data) - if wireless_controller_utm_profile_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_wireless_controller_utm_profile_data(wireless_controller_utm_profile_data)) + + if state == "present": return fos.set('wireless-controller', 'utm-profile', data=filtered_data, vdom=vdom) - elif wireless_controller_utm_profile_data['state'] == "absent": + elif state == "absent": return fos.delete('wireless-controller', 'utm-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_wireless_controller(data, fos): - login(data, fos) if data['wireless_controller_utm_profile']: resp = wireless_controller_utm_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"]}, "wireless_controller_utm_profile": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, - "antivirus-profile": {"required": False, "type": "str"}, - "application-list": {"required": False, "type": "str"}, + "antivirus_profile": {"required": False, "type": "str"}, + "application_list": {"required": False, "type": "str"}, "comment": {"required": False, "type": "str"}, - "ips-sensor": {"required": False, "type": "str"}, + "ips_sensor": {"required": False, "type": "str"}, "name": {"required": True, "type": "str"}, - "scan-botnet-connections": {"required": False, "type": "str", - "choices": ["disable", "block", "monitor"]}, - "utm-log": {"required": False, "type": "str", + "scan_botnet_connections": {"required": False, "type": "str", + "choices": ["disable", "monitor", "block"]}, + "utm_log": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "webfilter-profile": {"required": False, "type": "str"} + "webfilter_profile": {"required": False, "type": "str"} } } @@ -297,14 +328,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") - 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_wireless_controller(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_wireless_controller(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_wireless_controller(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_wireless_controller_vap.py b/lib/ansible/modules/network/fortios/fortios_wireless_controller_vap.py index 3168b7ca309..52a3972e858 100644 --- a/lib/ansible/modules/network/fortios/fortios_wireless_controller_vap.py +++ b/lib/ansible/modules/network/fortios/fortios_wireless_controller_vap.py @@ -26,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_wireless_controller_vap short_description: Configure Virtual Access Points (VAPs) in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify wireless_controller feature and vap category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -41,63 +41,81 @@ 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: 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 wireless_controller_vap: description: - Configure Virtual Access Points (VAPs). default: null + type: dict suboptions: - state: + acct_interim_interval: description: - - Indicates whether to create or remove the object - choices: - - present - - absent - acct-interim-interval: - description: - - WiFi RADIUS accounting interim interval (60 - 86400 sec, default = 0). + - WiFi RADIUS accounting interim interval (60 - 86400 sec). + type: int alias: description: - Alias. + type: str auth: description: - Authentication protocol. + type: str choices: - psk - radius - usergroup - broadcast-ssid: + broadcast_ssid: description: - - Enable/disable broadcasting the SSID (default = enable). + - Enable/disable broadcasting the SSID . + type: str choices: - enable - disable - broadcast-suppression: + broadcast_suppression: description: - Optional suppression of broadcast messages. For example, you can keep DHCP messages, ARP broadcasts, and so on off of the wireless network. + type: str choices: - dhcp-up - dhcp-down @@ -112,261 +130,315 @@ options: - ipv6 - all-other-mc - all-other-bc - captive-portal-ac-name: + captive_portal_ac_name: description: - Local-bridging captive portal ac-name. - captive-portal-macauth-radius-secret: + type: str + captive_portal_macauth_radius_secret: description: - Secret key to access the macauth RADIUS server. - captive-portal-macauth-radius-server: + type: str + captive_portal_macauth_radius_server: description: - Captive portal external RADIUS server domain name or IP address. - captive-portal-radius-secret: + type: str + captive_portal_radius_secret: description: - Secret key to access the RADIUS server. - captive-portal-radius-server: + type: str + captive_portal_radius_server: description: - Captive portal RADIUS server domain name or IP address. - captive-portal-session-timeout-interval: + type: str + captive_portal_session_timeout_interval: description: - - Session timeout interval (0 - 864000 sec, default = 0). - dhcp-lease-time: + - Session timeout interval (0 - 864000 sec). + type: int + dhcp_lease_time: description: - DHCP lease time in seconds for NAT IP address. - dhcp-option82-circuit-id-insertion: + type: int + dhcp_option82_circuit_id_insertion: description: - - Enable/disable DHCP option 82 circuit-id insert (default = disable). + - Enable/disable DHCP option 82 circuit-id insert . + type: str choices: - style-1 - style-2 - disable - dhcp-option82-insertion: + dhcp_option82_insertion: description: - - Enable/disable DHCP option 82 insert (default = disable). + - Enable/disable DHCP option 82 insert . + type: str choices: - enable - disable - dhcp-option82-remote-id-insertion: + dhcp_option82_remote_id_insertion: description: - - Enable/disable DHCP option 82 remote-id insert (default = disable). + - Enable/disable DHCP option 82 remote-id insert . + type: str choices: - style-1 - disable - dynamic-vlan: + dynamic_vlan: description: - Enable/disable dynamic VLAN assignment. + type: str choices: - enable - disable - eap-reauth: + eap_reauth: description: - Enable/disable EAP re-authentication for WPA-Enterprise security. + type: str choices: - enable - disable - eap-reauth-intv: + eap_reauth_intv: description: - - EAP re-authentication interval (1800 - 864000 sec, default = 86400). - eapol-key-retries: + - EAP re-authentication interval (1800 - 864000 sec). + type: int + eapol_key_retries: description: - - Enable/disable retransmission of EAPOL-Key frames (message 3/4 and group message 1/2) (default = enable). + - Enable/disable retransmission of EAPOL-Key frames (message 3/4 and group message 1/2) . + type: str choices: - disable - enable encrypt: description: - Encryption protocol to use (only available when security is set to a WPA type). + type: str choices: - TKIP - AES - TKIP-AES - external-fast-roaming: + external_fast_roaming: description: - - Enable/disable fast roaming or pre-authentication with external APs not managed by the FortiGate (default = disable). + - Enable/disable fast roaming or pre-authentication with external APs not managed by the FortiGate . + type: str choices: - enable - disable - external-logout: + external_logout: description: - URL of external authentication logout server. - external-web: + type: str + external_web: description: - URL of external authentication web server. - fast-bss-transition: + type: str + fast_bss_transition: description: - - Enable/disable 802.11r Fast BSS Transition (FT) (default = disable). + - Enable/disable 802.11r Fast BSS Transition (FT) . + type: str choices: - disable - enable - fast-roaming: + fast_roaming: description: - - Enable/disable fast-roaming, or pre-authentication, where supported by clients (default = disable). + - Enable/disable fast-roaming, or pre-authentication, where supported by clients . + type: str choices: - enable - disable - ft-mobility-domain: + ft_mobility_domain: description: - - Mobility domain identifier in FT (1 - 65535, default = 1000). - ft-over-ds: + - Mobility domain identifier in FT (1 - 65535). + type: int + ft_over_ds: description: - Enable/disable FT over the Distribution System (DS). + type: str choices: - disable - enable - ft-r0-key-lifetime: + ft_r0_key_lifetime: description: - Lifetime of the PMK-R0 key in FT, 1-65535 minutes. - gtk-rekey: + type: int + gtk_rekey: description: - Enable/disable GTK rekey for WPA security. + type: str choices: - enable - disable - gtk-rekey-intv: + gtk_rekey_intv: description: - - GTK rekey interval (1800 - 864000 sec, default = 86400). - hotspot20-profile: + - GTK rekey interval (1800 - 864000 sec). + type: int + hotspot20_profile: description: - Hotspot 2.0 profile name. - intra-vap-privacy: + type: str + intra_vap_privacy: description: - - Enable/disable blocking communication between clients on the same SSID (called intra-SSID privacy) (default = disable). + - Enable/disable blocking communication between clients on the same SSID (called intra-SSID privacy) . + type: str choices: - enable - disable ip: description: - IP address and subnet mask for the local standalone NAT subnet. + type: str key: description: - WEP Key. + type: str keyindex: description: - WEP key index (1 - 4). + type: int ldpc: description: - VAP low-density parity-check (LDPC) coding configuration. + type: str choices: - disable - rx - tx - rxtx - local-authentication: + local_authentication: description: - Enable/disable AP local authentication. + type: str choices: - enable - disable - local-bridging: + local_bridging: description: - - Enable/disable bridging of wireless and Ethernet interfaces on the FortiAP (default = disable). + - Enable/disable bridging of wireless and Ethernet interfaces on the FortiAP . + type: str choices: - enable - disable - local-lan: + local_lan: description: - - Allow/deny traffic destined for a Class A, B, or C private IP address (default = allow). + - Allow/deny traffic destined for a Class A, B, or C private IP address . + type: str choices: - allow - deny - local-standalone: + local_standalone: description: - - Enable/disable AP local standalone (default = disable). + - Enable/disable AP local standalone . + type: str choices: - enable - disable - local-standalone-nat: + local_standalone_nat: description: - Enable/disable AP local standalone NAT mode. + type: str choices: - enable - disable - mac-auth-bypass: + mac_auth_bypass: description: - Enable/disable MAC authentication bypass. + type: str choices: - enable - disable - mac-filter: + mac_filter: description: - Enable/disable MAC filtering to block wireless clients by mac address. + type: str choices: - enable - disable - mac-filter-list: + mac_filter_list: description: - Create a list of MAC addresses for MAC address filtering. + type: list suboptions: id: description: - ID. required: true + type: int mac: description: - MAC address. - mac-filter-policy: + type: str + mac_filter_policy: description: - Deny or allow the client with this MAC address. + type: str choices: - allow - deny - mac-filter-policy-other: + mac_filter_policy_other: description: - Allow or block clients with MAC addresses that are not in the filter list. + type: str choices: - allow - deny - max-clients: + max_clients: description: - - Maximum number of clients that can connect simultaneously to the VAP (default = 0, meaning no limitation). - max-clients-ap: + - Maximum number of clients that can connect simultaneously to the VAP . + type: int + max_clients_ap: description: - - Maximum number of clients that can connect simultaneously to each radio (default = 0, meaning no limitation). - me-disable-thresh: + - Maximum number of clients that can connect simultaneously to each radio . + type: int + me_disable_thresh: description: - Disable multicast enhancement when this many clients are receiving multicast traffic. - mesh-backhaul: + type: int + mesh_backhaul: description: - - Enable/disable using this VAP as a WiFi mesh backhaul (default = disable). This entry is only available when security is set to a WPA - type or open. + - Enable/disable using this VAP as a WiFi mesh backhaul . This entry is only available when security is set to a WPA type or open. + type: str choices: - enable - disable mpsk: description: - Enable/disable multiple pre-shared keys (PSKs.) + type: str choices: - enable - disable - mpsk-concurrent-clients: + mpsk_concurrent_clients: description: - Number of pre-shared keys (PSKs) to allow if multiple pre-shared keys are enabled. - mpsk-key: + type: int + mpsk_key: description: - Pre-shared keys that can be used to connect to this virtual access point. + type: list suboptions: comment: description: - Comment. - concurrent-clients: + type: str + concurrent_clients: description: - Number of clients that can connect using this pre-shared key. - key-name: + type: str + key_name: description: - Pre-shared key name. - required: true + type: str passphrase: description: - WPA Pre-shared key. - multicast-enhance: + type: str + multicast_enhance: description: - - Enable/disable converting multicast to unicast to improve performance (default = disable). + - Enable/disable converting multicast to unicast to improve performance . + type: str choices: - enable - disable - multicast-rate: + multicast_rate: description: - - Multicast rate (0, 6000, 12000, or 24000 kbps, default = 0). + - Multicast rate (0, 6000, 12000, or 24000 kbps). + type: str choices: - 0 - 6000 @@ -376,50 +448,63 @@ options: description: - Virtual AP name. required: true + type: str okc: description: - - Enable/disable Opportunistic Key Caching (OKC) (default = enable). + - Enable/disable Opportunistic Key Caching (OKC) . + type: str choices: - disable - enable passphrase: description: - WPA pre-shard key (PSK) to be used to authenticate WiFi users. + type: str pmf: description: - - Protected Management Frames (PMF) support (default = disable). + - Protected Management Frames (PMF) support . + type: str choices: - disable - enable - optional - pmf-assoc-comeback-timeout: + pmf_assoc_comeback_timeout: description: - Protected Management Frames (PMF) comeback maximum timeout (1-20 sec). - pmf-sa-query-retry-timeout: + type: int + pmf_sa_query_retry_timeout: description: - Protected Management Frames (PMF) SA query retry timeout interval (1 - 5 100s of msec). - portal-message-override-group: + type: int + portal_message_override_group: description: - Replacement message group for this VAP (only available when security is set to a captive portal type). - portal-message-overrides: + type: str + portal_message_overrides: description: - Individual message overrides. + type: dict suboptions: - auth-disclaimer-page: + auth_disclaimer_page: description: - Override auth-disclaimer-page message with message from portal-message-overrides group. - auth-login-failed-page: + type: str + auth_login_failed_page: description: - Override auth-login-failed-page message with message from portal-message-overrides group. - auth-login-page: + type: str + auth_login_page: description: - Override auth-login-page message with message from portal-message-overrides group. - auth-reject-page: + type: str + auth_reject_page: description: - Override auth-reject-page message with message from portal-message-overrides group. - portal-type: + type: str + portal_type: description: - Captive portal functionality. Configure how the captive portal authenticates users and whether it includes a disclaimer. + type: str choices: - auth - auth+disclaimer @@ -428,68 +513,83 @@ options: - cmcc - cmcc-macauth - auth-mac - probe-resp-suppression: + probe_resp_suppression: description: - - Enable/disable probe response suppression (to ignore weak signals) (default = disable). + - Enable/disable probe response suppression (to ignore weak signals) . + type: str choices: - enable - disable - probe-resp-threshold: + probe_resp_threshold: description: - - Minimum signal level/threshold in dBm required for the AP response to probe requests (-95 to -20, default = -80). - ptk-rekey: + - Minimum signal level/threshold in dBm required for the AP response to probe requests (-95 to -20). + type: str + ptk_rekey: description: - Enable/disable PTK rekey for WPA-Enterprise security. + type: str choices: - enable - disable - ptk-rekey-intv: + ptk_rekey_intv: description: - - PTK rekey interval (1800 - 864000 sec, default = 86400). - qos-profile: + - PTK rekey interval (1800 - 864000 sec). + type: int + qos_profile: description: - Quality of service profile name. + type: str quarantine: description: - - Enable/disable station quarantine (default = enable). + - Enable/disable station quarantine . + type: str choices: - enable - disable - radio-2g-threshold: + radio_2g_threshold: description: - - Minimum signal level/threshold in dBm required for the AP response to receive a packet in 2.4G band (-95 to -20, default = -79). - radio-5g-threshold: + - Minimum signal level/threshold in dBm required for the AP response to receive a packet in 2.4G band (-95 to -20). + type: str + radio_5g_threshold: description: - - Minimum signal level/threshold in dBm required for the AP response to receive a packet in 5G band(-95 to -20, default = -76). - radio-sensitivity: + - Minimum signal level/threshold in dBm required for the AP response to receive a packet in 5G band(-95 to -20). + type: str + radio_sensitivity: description: - - Enable/disable software radio sensitivity (to ignore weak signals) (default = disable). + - Enable/disable software radio sensitivity (to ignore weak signals) . + type: str choices: - enable - disable - radius-mac-auth: + radius_mac_auth: description: - - Enable/disable RADIUS-based MAC authentication of clients (default = disable). + - Enable/disable RADIUS-based MAC authentication of clients . + type: str choices: - enable - disable - radius-mac-auth-server: + radius_mac_auth_server: description: - RADIUS-based MAC authentication server. - radius-mac-auth-usergroups: + type: str + radius_mac_auth_usergroups: description: - Selective user groups that are permitted for RADIUS mac authentication. + type: list suboptions: name: description: - User group name. required: true - radius-server: + type: str + radius_server: description: - RADIUS server to be used to authenticate WiFi users. - rates-11a: + type: str + rates_11a: description: - Allowed data rates for 802.11a. + type: str choices: - 1 - 1-basic @@ -515,9 +615,10 @@ options: - 48-basic - 54 - 54-basic - rates-11ac-ss12: + rates_11ac_ss12: description: - Allowed data rates for 802.11ac with 1 or 2 spatial streams. + type: str choices: - mcs0/1 - mcs1/1 @@ -543,9 +644,10 @@ options: - mcs9/2 - mcs10/2 - mcs11/2 - rates-11ac-ss34: + rates_11ac_ss34: description: - Allowed data rates for 802.11ac with 3 or 4 spatial streams. + type: str choices: - mcs0/3 - mcs1/3 @@ -571,9 +673,10 @@ options: - mcs9/4 - mcs10/4 - mcs11/4 - rates-11bg: + rates_11bg: description: - Allowed data rates for 802.11b/g. + type: str choices: - 1 - 1-basic @@ -599,9 +702,10 @@ options: - 48-basic - 54 - 54-basic - rates-11n-ss12: + rates_11n_ss12: description: - Allowed data rates for 802.11n with 1 or 2 spatial streams. + type: str choices: - mcs0/1 - mcs1/1 @@ -619,9 +723,10 @@ options: - mcs13/2 - mcs14/2 - mcs15/2 - rates-11n-ss34: + rates_11n_ss34: description: - Allowed data rates for 802.11n with 3 or 4 spatial streams. + type: str choices: - mcs16/3 - mcs17/3 @@ -642,9 +747,11 @@ options: schedule: description: - VAP schedule name. + type: str security: description: - - Security mode for the wireless interface (default = wpa2-only-personal). + - Security mode for the wireless interface . + type: str choices: - open - captive-portal @@ -660,29 +767,35 @@ options: - wpa2-only-personal+captive-portal - wpa2-only-enterprise - osen - security-exempt-list: + security_exempt_list: description: - Optional security exempt list for captive portal authentication. - security-obsolete-option: + type: str + security_obsolete_option: description: - Enable/disable obsolete security options. + type: str choices: - enable - disable - security-redirect-url: + security_redirect_url: description: - Optional URL for redirecting users after they pass captive portal authentication. - selected-usergroups: + type: str + selected_usergroups: description: - Selective user groups that are permitted to authenticate. + type: list suboptions: name: description: - User group name. required: true - split-tunneling: + type: str + split_tunneling: description: - - Enable/disable split tunneling (default = disable). + - Enable/disable split tunneling . + type: str choices: - enable - disable @@ -690,47 +803,58 @@ options: description: - IEEE 802.11 service set identifier (SSID) for the wireless interface. Users who wish to use the wireless network must configure their computers to access this SSID name. - tkip-counter-measure: + type: str + tkip_counter_measure: description: - Enable/disable TKIP counter measure. + type: str choices: - enable - disable usergroup: description: - Firewall user group to be used to authenticate WiFi users. + type: list suboptions: name: description: - User group name. required: true - utm-profile: + type: str + utm_profile: description: - UTM profile name. + type: str vdom: description: - Name of the VDOM that the Virtual AP has been added to. Source system.vdom.name. - vlan-auto: + type: str + vlan_auto: description: - Enable/disable automatic management of SSID VLAN interface. + type: str choices: - enable - disable - vlan-pool: + vlan_pool: description: - VLAN pool. + type: list suboptions: id: description: - ID. required: true - wtp-group: + type: int + wtp_group: description: - WTP group name. - vlan-pooling: + type: str + vlan_pooling: description: - - Enable/disable VLAN pooling, to allow grouping of multiple wireless controller VLANs into VLAN pools (default = disable). When set to - wtp-group, VLAN pooling occurs with VLAN assignment by wtp-group. + - Enable/disable VLAN pooling, to allow grouping of multiple wireless controller VLANs into VLAN pools . When set to wtp-group, VLAN + pooling occurs with VLAN assignment by wtp-group. + type: str choices: - wtp-group - round-robin @@ -739,9 +863,11 @@ options: vlanid: description: - Optional VLAN ID. - voice-enterprise: + type: int + voice_enterprise: description: - - Enable/disable 802.11k and 802.11v assisted Voice-Enterprise roaming (default = disable). + - Enable/disable 802.11k and 802.11v assisted Voice-Enterprise roaming . + type: str choices: - disable - enable @@ -754,6 +880,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure Virtual Access Points (VAPs). fortios_wireless_controller_vap: @@ -762,129 +889,129 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" wireless_controller_vap: - state: "present" - acct-interim-interval: "3" + acct_interim_interval: "3" alias: "" auth: "psk" - broadcast-ssid: "enable" - broadcast-suppression: "dhcp-up" - captive-portal-ac-name: "" - captive-portal-macauth-radius-secret: "" - captive-portal-macauth-radius-server: "" - captive-portal-radius-secret: "" - captive-portal-radius-server: "" - captive-portal-session-timeout-interval: "13" - dhcp-lease-time: "14" - dhcp-option82-circuit-id-insertion: "style-1" - dhcp-option82-insertion: "enable" - dhcp-option82-remote-id-insertion: "style-1" - dynamic-vlan: "enable" - eap-reauth: "enable" - eap-reauth-intv: "20" - eapol-key-retries: "disable" + broadcast_ssid: "enable" + broadcast_suppression: "dhcp-up" + captive_portal_ac_name: "" + captive_portal_macauth_radius_secret: "" + captive_portal_macauth_radius_server: "" + captive_portal_radius_secret: "" + captive_portal_radius_server: "" + captive_portal_session_timeout_interval: "13" + dhcp_lease_time: "14" + dhcp_option82_circuit_id_insertion: "style-1" + dhcp_option82_insertion: "enable" + dhcp_option82_remote_id_insertion: "style-1" + dynamic_vlan: "enable" + eap_reauth: "enable" + eap_reauth_intv: "20" + eapol_key_retries: "disable" encrypt: "TKIP" - external-fast-roaming: "enable" - external-logout: "" - external-web: "" - fast-bss-transition: "disable" - fast-roaming: "enable" - ft-mobility-domain: "28" - ft-over-ds: "disable" - ft-r0-key-lifetime: "30" - gtk-rekey: "enable" - gtk-rekey-intv: "32" - hotspot20-profile: "" - intra-vap-privacy: "enable" + external_fast_roaming: "enable" + external_logout: "" + external_web: "" + fast_bss_transition: "disable" + fast_roaming: "enable" + ft_mobility_domain: "28" + ft_over_ds: "disable" + ft_r0_key_lifetime: "30" + gtk_rekey: "enable" + gtk_rekey_intv: "32" + hotspot20_profile: "" + intra_vap_privacy: "enable" ip: "" key: "" keyindex: "37" ldpc: "disable" - local-authentication: "enable" - local-bridging: "enable" - local-lan: "allow" - local-standalone: "enable" - local-standalone-nat: "enable" - mac-auth-bypass: "enable" - mac-filter: "enable" - mac-filter-list: + local_authentication: "enable" + local_bridging: "enable" + local_lan: "allow" + local_standalone: "enable" + local_standalone_nat: "enable" + mac_auth_bypass: "enable" + mac_filter: "enable" + mac_filter_list: - id: "47" mac: "" - mac-filter-policy: "allow" - mac-filter-policy-other: "allow" - max-clients: "51" - max-clients-ap: "52" - me-disable-thresh: "53" - mesh-backhaul: "enable" + mac_filter_policy: "allow" + mac_filter_policy_other: "allow" + max_clients: "51" + max_clients_ap: "52" + me_disable_thresh: "53" + mesh_backhaul: "enable" mpsk: "enable" - mpsk-concurrent-clients: "56" - mpsk-key: + mpsk_concurrent_clients: "56" + mpsk_key: - comment: "Comment." - concurrent-clients: "" - key-name: "" + concurrent_clients: "" + key_name: "" passphrase: "" - multicast-enhance: "enable" - multicast-rate: "0" + multicast_enhance: "enable" + multicast_rate: "0" name: "default_name_64" okc: "disable" passphrase: "" pmf: "disable" - pmf-assoc-comeback-timeout: "68" - pmf-sa-query-retry-timeout: "69" - portal-message-override-group: "" - portal-message-overrides: - auth-disclaimer-page: "" - auth-login-failed-page: "" - auth-login-page: "" - auth-reject-page: "" - portal-type: "auth" - probe-resp-suppression: "enable" - probe-resp-threshold: "" - ptk-rekey: "enable" - ptk-rekey-intv: "80" - qos-profile: "" + pmf_assoc_comeback_timeout: "68" + pmf_sa_query_retry_timeout: "69" + portal_message_override_group: "" + portal_message_overrides: + auth_disclaimer_page: "" + auth_login_failed_page: "" + auth_login_page: "" + auth_reject_page: "" + portal_type: "auth" + probe_resp_suppression: "enable" + probe_resp_threshold: "" + ptk_rekey: "enable" + ptk_rekey_intv: "80" + qos_profile: "" quarantine: "enable" - radio-2g-threshold: "" - radio-5g-threshold: "" - radio-sensitivity: "enable" - radius-mac-auth: "enable" - radius-mac-auth-server: "" - radius-mac-auth-usergroups: + radio_2g_threshold: "" + radio_5g_threshold: "" + radio_sensitivity: "enable" + radius_mac_auth: "enable" + radius_mac_auth_server: "" + radius_mac_auth_usergroups: - name: "default_name_89" - radius-server: "" - rates-11a: "1" - rates-11ac-ss12: "mcs0/1" - rates-11ac-ss34: "mcs0/3" - rates-11bg: "1" - rates-11n-ss12: "mcs0/1" - rates-11n-ss34: "mcs16/3" + radius_server: "" + rates_11a: "1" + rates_11ac_ss12: "mcs0/1" + rates_11ac_ss34: "mcs0/3" + rates_11bg: "1" + rates_11n_ss12: "mcs0/1" + rates_11n_ss34: "mcs16/3" schedule: "" security: "open" - security-exempt-list: "" - security-obsolete-option: "enable" - security-redirect-url: "" - selected-usergroups: + security_exempt_list: "" + security_obsolete_option: "enable" + security_redirect_url: "" + selected_usergroups: - name: "default_name_103" - split-tunneling: "enable" + split_tunneling: "enable" ssid: "" - tkip-counter-measure: "enable" + tkip_counter_measure: "enable" usergroup: - name: "default_name_108" - utm-profile: "" + utm_profile: "" vdom: " (source system.vdom.name)" - vlan-auto: "enable" - vlan-pool: + vlan_auto: "enable" + vlan_pool: - id: "113" - wtp-group: "" - vlan-pooling: "wtp-group" + wtp_group: "" + vlan_pooling: "wtp-group" vlanid: "116" - voice-enterprise: "disable" + voice_enterprise: "disable" ''' RETURN = ''' @@ -947,12 +1074,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 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']: @@ -960,43 +1091,43 @@ def login(data, fos): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_wireless_controller_vap_data(json): - option_list = ['acct-interim-interval', 'alias', 'auth', - 'broadcast-ssid', 'broadcast-suppression', 'captive-portal-ac-name', - 'captive-portal-macauth-radius-secret', 'captive-portal-macauth-radius-server', 'captive-portal-radius-secret', - 'captive-portal-radius-server', 'captive-portal-session-timeout-interval', 'dhcp-lease-time', - 'dhcp-option82-circuit-id-insertion', 'dhcp-option82-insertion', 'dhcp-option82-remote-id-insertion', - 'dynamic-vlan', 'eap-reauth', 'eap-reauth-intv', - 'eapol-key-retries', 'encrypt', 'external-fast-roaming', - 'external-logout', 'external-web', 'fast-bss-transition', - 'fast-roaming', 'ft-mobility-domain', 'ft-over-ds', - 'ft-r0-key-lifetime', 'gtk-rekey', 'gtk-rekey-intv', - 'hotspot20-profile', 'intra-vap-privacy', 'ip', + option_list = ['acct_interim_interval', 'alias', 'auth', + 'broadcast_ssid', 'broadcast_suppression', 'captive_portal_ac_name', + 'captive_portal_macauth_radius_secret', 'captive_portal_macauth_radius_server', 'captive_portal_radius_secret', + 'captive_portal_radius_server', 'captive_portal_session_timeout_interval', 'dhcp_lease_time', + 'dhcp_option82_circuit_id_insertion', 'dhcp_option82_insertion', 'dhcp_option82_remote_id_insertion', + 'dynamic_vlan', 'eap_reauth', 'eap_reauth_intv', + 'eapol_key_retries', 'encrypt', 'external_fast_roaming', + 'external_logout', 'external_web', 'fast_bss_transition', + 'fast_roaming', 'ft_mobility_domain', 'ft_over_ds', + 'ft_r0_key_lifetime', 'gtk_rekey', 'gtk_rekey_intv', + 'hotspot20_profile', 'intra_vap_privacy', 'ip', 'key', 'keyindex', 'ldpc', - 'local-authentication', 'local-bridging', 'local-lan', - 'local-standalone', 'local-standalone-nat', 'mac-auth-bypass', - 'mac-filter', 'mac-filter-list', 'mac-filter-policy-other', - 'max-clients', 'max-clients-ap', 'me-disable-thresh', - 'mesh-backhaul', 'mpsk', 'mpsk-concurrent-clients', - 'mpsk-key', 'multicast-enhance', 'multicast-rate', + 'local_authentication', 'local_bridging', 'local_lan', + 'local_standalone', 'local_standalone_nat', 'mac_auth_bypass', + 'mac_filter', 'mac_filter_list', 'mac_filter_policy_other', + 'max_clients', 'max_clients_ap', 'me_disable_thresh', + 'mesh_backhaul', 'mpsk', 'mpsk_concurrent_clients', + 'mpsk_key', 'multicast_enhance', 'multicast_rate', 'name', 'okc', 'passphrase', - 'pmf', 'pmf-assoc-comeback-timeout', 'pmf-sa-query-retry-timeout', - 'portal-message-override-group', 'portal-message-overrides', 'portal-type', - 'probe-resp-suppression', 'probe-resp-threshold', 'ptk-rekey', - 'ptk-rekey-intv', 'qos-profile', 'quarantine', - 'radio-2g-threshold', 'radio-5g-threshold', 'radio-sensitivity', - 'radius-mac-auth', 'radius-mac-auth-server', 'radius-mac-auth-usergroups', - 'radius-server', 'rates-11a', 'rates-11ac-ss12', - 'rates-11ac-ss34', 'rates-11bg', 'rates-11n-ss12', - 'rates-11n-ss34', 'schedule', 'security', - 'security-exempt-list', 'security-obsolete-option', 'security-redirect-url', - 'selected-usergroups', 'split-tunneling', 'ssid', - 'tkip-counter-measure', 'usergroup', 'utm-profile', - 'vdom', 'vlan-auto', 'vlan-pool', - 'vlan-pooling', 'vlanid', 'voice-enterprise'] + 'pmf', 'pmf_assoc_comeback_timeout', 'pmf_sa_query_retry_timeout', + 'portal_message_override_group', 'portal_message_overrides', 'portal_type', + 'probe_resp_suppression', 'probe_resp_threshold', 'ptk_rekey', + 'ptk_rekey_intv', 'qos_profile', 'quarantine', + 'radio_2g_threshold', 'radio_5g_threshold', 'radio_sensitivity', + 'radius_mac_auth', 'radius_mac_auth_server', 'radius_mac_auth_usergroups', + 'radius_server', 'rates_11a', 'rates_11ac_ss12', + 'rates_11ac_ss34', 'rates_11bg', 'rates_11n_ss12', + 'rates_11n_ss34', 'schedule', 'security', + 'security_exempt_list', 'security_obsolete_option', 'security_redirect_url', + 'selected_usergroups', 'split_tunneling', 'ssid', + 'tkip_counter_measure', 'usergroup', 'utm_profile', + 'vdom', 'vlan_auto', 'vlan_pool', + 'vlan_pooling', 'vlanid', 'voice_enterprise'] dictionary = {} for attribute in option_list: @@ -1006,112 +1137,117 @@ def filter_wireless_controller_vap_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +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 wireless_controller_vap(data, fos): vdom = data['vdom'] + state = data['state'] wireless_controller_vap_data = data['wireless_controller_vap'] - flattened_data = flatten_multilists_attributes(wireless_controller_vap_data) - filtered_data = filter_wireless_controller_vap_data(flattened_data) - if wireless_controller_vap_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_wireless_controller_vap_data(wireless_controller_vap_data)) + + if state == "present": return fos.set('wireless-controller', 'vap', data=filtered_data, vdom=vdom) - elif wireless_controller_vap_data['state'] == "absent": + elif state == "absent": return fos.delete('wireless-controller', 'vap', 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_wireless_controller(data, fos): - login(data, fos) if data['wireless_controller_vap']: resp = wireless_controller_vap(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"]}, "wireless_controller_vap": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, - "acct-interim-interval": {"required": False, "type": "int"}, + "acct_interim_interval": {"required": False, "type": "int"}, "alias": {"required": False, "type": "str"}, "auth": {"required": False, "type": "str", "choices": ["psk", "radius", "usergroup"]}, - "broadcast-ssid": {"required": False, "type": "str", + "broadcast_ssid": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "broadcast-suppression": {"required": False, "type": "str", + "broadcast_suppression": {"required": False, "type": "str", "choices": ["dhcp-up", "dhcp-down", "dhcp-starvation", "arp-known", "arp-unknown", "arp-reply", "arp-poison", "arp-proxy", "netbios-ns", "netbios-ds", "ipv6", "all-other-mc", "all-other-bc"]}, - "captive-portal-ac-name": {"required": False, "type": "str"}, - "captive-portal-macauth-radius-secret": {"required": False, "type": "str"}, - "captive-portal-macauth-radius-server": {"required": False, "type": "str"}, - "captive-portal-radius-secret": {"required": False, "type": "str"}, - "captive-portal-radius-server": {"required": False, "type": "str"}, - "captive-portal-session-timeout-interval": {"required": False, "type": "int"}, - "dhcp-lease-time": {"required": False, "type": "int"}, - "dhcp-option82-circuit-id-insertion": {"required": False, "type": "str", + "captive_portal_ac_name": {"required": False, "type": "str"}, + "captive_portal_macauth_radius_secret": {"required": False, "type": "str"}, + "captive_portal_macauth_radius_server": {"required": False, "type": "str"}, + "captive_portal_radius_secret": {"required": False, "type": "str"}, + "captive_portal_radius_server": {"required": False, "type": "str"}, + "captive_portal_session_timeout_interval": {"required": False, "type": "int"}, + "dhcp_lease_time": {"required": False, "type": "int"}, + "dhcp_option82_circuit_id_insertion": {"required": False, "type": "str", "choices": ["style-1", "style-2", "disable"]}, - "dhcp-option82-insertion": {"required": False, "type": "str", + "dhcp_option82_insertion": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "dhcp-option82-remote-id-insertion": {"required": False, "type": "str", + "dhcp_option82_remote_id_insertion": {"required": False, "type": "str", "choices": ["style-1", "disable"]}, - "dynamic-vlan": {"required": False, "type": "str", + "dynamic_vlan": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "eap-reauth": {"required": False, "type": "str", + "eap_reauth": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "eap-reauth-intv": {"required": False, "type": "int"}, - "eapol-key-retries": {"required": False, "type": "str", + "eap_reauth_intv": {"required": False, "type": "int"}, + "eapol_key_retries": {"required": False, "type": "str", "choices": ["disable", "enable"]}, "encrypt": {"required": False, "type": "str", "choices": ["TKIP", "AES", "TKIP-AES"]}, - "external-fast-roaming": {"required": False, "type": "str", + "external_fast_roaming": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "external-logout": {"required": False, "type": "str"}, - "external-web": {"required": False, "type": "str"}, - "fast-bss-transition": {"required": False, "type": "str", + "external_logout": {"required": False, "type": "str"}, + "external_web": {"required": False, "type": "str"}, + "fast_bss_transition": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "fast-roaming": {"required": False, "type": "str", + "fast_roaming": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ft-mobility-domain": {"required": False, "type": "int"}, - "ft-over-ds": {"required": False, "type": "str", + "ft_mobility_domain": {"required": False, "type": "int"}, + "ft_over_ds": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "ft-r0-key-lifetime": {"required": False, "type": "int"}, - "gtk-rekey": {"required": False, "type": "str", + "ft_r0_key_lifetime": {"required": False, "type": "int"}, + "gtk_rekey": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "gtk-rekey-intv": {"required": False, "type": "int"}, - "hotspot20-profile": {"required": False, "type": "str"}, - "intra-vap-privacy": {"required": False, "type": "str", + "gtk_rekey_intv": {"required": False, "type": "int"}, + "hotspot20_profile": {"required": False, "type": "str"}, + "intra_vap_privacy": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "ip": {"required": False, "type": "str"}, "key": {"required": False, "type": "str"}, @@ -1119,47 +1255,47 @@ def main(): "ldpc": {"required": False, "type": "str", "choices": ["disable", "rx", "tx", "rxtx"]}, - "local-authentication": {"required": False, "type": "str", + "local_authentication": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "local-bridging": {"required": False, "type": "str", + "local_bridging": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "local-lan": {"required": False, "type": "str", + "local_lan": {"required": False, "type": "str", "choices": ["allow", "deny"]}, - "local-standalone": {"required": False, "type": "str", + "local_standalone": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "local-standalone-nat": {"required": False, "type": "str", + "local_standalone_nat": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "mac-auth-bypass": {"required": False, "type": "str", + "mac_auth_bypass": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "mac-filter": {"required": False, "type": "str", + "mac_filter": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "mac-filter-list": {"required": False, "type": "list", + "mac_filter_list": {"required": False, "type": "list", "options": { "id": {"required": True, "type": "int"}, "mac": {"required": False, "type": "str"}, - "mac-filter-policy": {"required": False, "type": "str", + "mac_filter_policy": {"required": False, "type": "str", "choices": ["allow", "deny"]} }}, - "mac-filter-policy-other": {"required": False, "type": "str", + "mac_filter_policy_other": {"required": False, "type": "str", "choices": ["allow", "deny"]}, - "max-clients": {"required": False, "type": "int"}, - "max-clients-ap": {"required": False, "type": "int"}, - "me-disable-thresh": {"required": False, "type": "int"}, - "mesh-backhaul": {"required": False, "type": "str", + "max_clients": {"required": False, "type": "int"}, + "max_clients_ap": {"required": False, "type": "int"}, + "me_disable_thresh": {"required": False, "type": "int"}, + "mesh_backhaul": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "mpsk": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "mpsk-concurrent-clients": {"required": False, "type": "int"}, - "mpsk-key": {"required": False, "type": "list", + "mpsk_concurrent_clients": {"required": False, "type": "int"}, + "mpsk_key": {"required": False, "type": "list", "options": { "comment": {"required": False, "type": "str"}, - "concurrent-clients": {"required": False, "type": "str"}, - "key-name": {"required": True, "type": "str"}, + "concurrent_clients": {"required": False, "type": "str"}, + "key_name": {"required": False, "type": "str"}, "passphrase": {"required": False, "type": "str"} }}, - "multicast-enhance": {"required": False, "type": "str", + "multicast_enhance": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "multicast-rate": {"required": False, "type": "str", + "multicast_rate": {"required": False, "type": "str", "choices": ["0", "6000", "12000", "24000"]}, "name": {"required": True, "type": "str"}, @@ -1168,42 +1304,42 @@ def main(): "passphrase": {"required": False, "type": "str"}, "pmf": {"required": False, "type": "str", "choices": ["disable", "enable", "optional"]}, - "pmf-assoc-comeback-timeout": {"required": False, "type": "int"}, - "pmf-sa-query-retry-timeout": {"required": False, "type": "int"}, - "portal-message-override-group": {"required": False, "type": "str"}, - "portal-message-overrides": {"required": False, "type": "dict", + "pmf_assoc_comeback_timeout": {"required": False, "type": "int"}, + "pmf_sa_query_retry_timeout": {"required": False, "type": "int"}, + "portal_message_override_group": {"required": False, "type": "str"}, + "portal_message_overrides": {"required": False, "type": "dict", "options": { - "auth-disclaimer-page": {"required": False, "type": "str"}, - "auth-login-failed-page": {"required": False, "type": "str"}, - "auth-login-page": {"required": False, "type": "str"}, - "auth-reject-page": {"required": False, "type": "str"} + "auth_disclaimer_page": {"required": False, "type": "str"}, + "auth_login_failed_page": {"required": False, "type": "str"}, + "auth_login_page": {"required": False, "type": "str"}, + "auth_reject_page": {"required": False, "type": "str"} }}, - "portal-type": {"required": False, "type": "str", + "portal_type": {"required": False, "type": "str", "choices": ["auth", "auth+disclaimer", "disclaimer", "email-collect", "cmcc", "cmcc-macauth", "auth-mac"]}, - "probe-resp-suppression": {"required": False, "type": "str", + "probe_resp_suppression": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "probe-resp-threshold": {"required": False, "type": "str"}, - "ptk-rekey": {"required": False, "type": "str", + "probe_resp_threshold": {"required": False, "type": "str"}, + "ptk_rekey": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ptk-rekey-intv": {"required": False, "type": "int"}, - "qos-profile": {"required": False, "type": "str"}, + "ptk_rekey_intv": {"required": False, "type": "int"}, + "qos_profile": {"required": False, "type": "str"}, "quarantine": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "radio-2g-threshold": {"required": False, "type": "str"}, - "radio-5g-threshold": {"required": False, "type": "str"}, - "radio-sensitivity": {"required": False, "type": "str", + "radio_2g_threshold": {"required": False, "type": "str"}, + "radio_5g_threshold": {"required": False, "type": "str"}, + "radio_sensitivity": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "radius-mac-auth": {"required": False, "type": "str", + "radius_mac_auth": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "radius-mac-auth-server": {"required": False, "type": "str"}, - "radius-mac-auth-usergroups": {"required": False, "type": "list", + "radius_mac_auth_server": {"required": False, "type": "str"}, + "radius_mac_auth_usergroups": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, - "radius-server": {"required": False, "type": "str"}, - "rates-11a": {"required": False, "type": "str", + "radius_server": {"required": False, "type": "str"}, + "rates_11a": {"required": False, "type": "str", "choices": ["1", "1-basic", "2", "2-basic", "5.5", "5.5-basic", "11", "11-basic", "6", @@ -1212,7 +1348,7 @@ def main(): "18-basic", "24", "24-basic", "36", "36-basic", "48", "48-basic", "54", "54-basic"]}, - "rates-11ac-ss12": {"required": False, "type": "str", + "rates_11ac_ss12": {"required": False, "type": "str", "choices": ["mcs0/1", "mcs1/1", "mcs2/1", "mcs3/1", "mcs4/1", "mcs5/1", "mcs6/1", "mcs7/1", "mcs8/1", @@ -1221,7 +1357,7 @@ def main(): "mcs3/2", "mcs4/2", "mcs5/2", "mcs6/2", "mcs7/2", "mcs8/2", "mcs9/2", "mcs10/2", "mcs11/2"]}, - "rates-11ac-ss34": {"required": False, "type": "str", + "rates_11ac_ss34": {"required": False, "type": "str", "choices": ["mcs0/3", "mcs1/3", "mcs2/3", "mcs3/3", "mcs4/3", "mcs5/3", "mcs6/3", "mcs7/3", "mcs8/3", @@ -1230,7 +1366,7 @@ def main(): "mcs3/4", "mcs4/4", "mcs5/4", "mcs6/4", "mcs7/4", "mcs8/4", "mcs9/4", "mcs10/4", "mcs11/4"]}, - "rates-11bg": {"required": False, "type": "str", + "rates_11bg": {"required": False, "type": "str", "choices": ["1", "1-basic", "2", "2-basic", "5.5", "5.5-basic", "11", "11-basic", "6", @@ -1239,14 +1375,14 @@ def main(): "18-basic", "24", "24-basic", "36", "36-basic", "48", "48-basic", "54", "54-basic"]}, - "rates-11n-ss12": {"required": False, "type": "str", + "rates_11n_ss12": {"required": False, "type": "str", "choices": ["mcs0/1", "mcs1/1", "mcs2/1", "mcs3/1", "mcs4/1", "mcs5/1", "mcs6/1", "mcs7/1", "mcs8/2", "mcs9/2", "mcs10/2", "mcs11/2", "mcs12/2", "mcs13/2", "mcs14/2", "mcs15/2"]}, - "rates-11n-ss34": {"required": False, "type": "str", + "rates_11n_ss34": {"required": False, "type": "str", "choices": ["mcs16/3", "mcs17/3", "mcs18/3", "mcs19/3", "mcs20/3", "mcs21/3", "mcs22/3", "mcs23/3", "mcs24/4", @@ -1260,37 +1396,37 @@ def main(): "wpa-enterprise", "wpa-only-personal", "wpa-only-personal+captive-portal", "wpa-only-enterprise", "wpa2-only-personal", "wpa2-only-personal+captive-portal", "wpa2-only-enterprise", "osen"]}, - "security-exempt-list": {"required": False, "type": "str"}, - "security-obsolete-option": {"required": False, "type": "str", + "security_exempt_list": {"required": False, "type": "str"}, + "security_obsolete_option": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "security-redirect-url": {"required": False, "type": "str"}, - "selected-usergroups": {"required": False, "type": "list", + "security_redirect_url": {"required": False, "type": "str"}, + "selected_usergroups": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, - "split-tunneling": {"required": False, "type": "str", + "split_tunneling": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "ssid": {"required": False, "type": "str"}, - "tkip-counter-measure": {"required": False, "type": "str", + "tkip_counter_measure": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "usergroup": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, - "utm-profile": {"required": False, "type": "str"}, + "utm_profile": {"required": False, "type": "str"}, "vdom": {"required": False, "type": "str"}, - "vlan-auto": {"required": False, "type": "str", + "vlan_auto": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "vlan-pool": {"required": False, "type": "list", + "vlan_pool": {"required": False, "type": "list", "options": { "id": {"required": True, "type": "int"}, - "wtp-group": {"required": False, "type": "str"} + "wtp_group": {"required": False, "type": "str"} }}, - "vlan-pooling": {"required": False, "type": "str", + "vlan_pooling": {"required": False, "type": "str", "choices": ["wtp-group", "round-robin", "hash", "disable"]}, "vlanid": {"required": False, "type": "int"}, - "voice-enterprise": {"required": False, "type": "str", + "voice_enterprise": {"required": False, "type": "str", "choices": ["disable", "enable"]} } @@ -1299,14 +1435,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") - 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_wireless_controller(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_wireless_controller(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_wireless_controller(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_wireless_controller_wids_profile.py b/lib/ansible/modules/network/fortios/fortios_wireless_controller_wids_profile.py index f0b67a3f34e..f643128cdfa 100644 --- a/lib/ansible/modules/network/fortios/fortios_wireless_controller_wids_profile.py +++ b/lib/ansible/modules/network/fortios/fortios_wireless_controller_wids_profile.py @@ -26,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_wireless_controller_wids_profile short_description: Configure wireless intrusion detection system (WIDS) profiles in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify wireless_controller feature and wids_profile category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -41,49 +41,64 @@ 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: 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 wireless_controller_wids_profile: description: - Configure wireless intrusion detection system (WIDS) profiles. default: null + type: dict suboptions: - state: + ap_auto_suppress: description: - - Indicates whether to create or remove the object - choices: - - present - - absent - ap-auto-suppress: - description: - - Enable/disable on-wire rogue AP auto-suppression (default = disable). + - Enable/disable on-wire rogue AP auto-suppression . + type: str choices: - enable - disable - ap-bgscan-disable-day: + ap_bgscan_disable_day: description: - Optionally turn off scanning for one or more days of the week. Separate the days with a space. By default, no days are set. + type: str choices: - sunday - monday @@ -92,203 +107,250 @@ options: - thursday - friday - saturday - ap-bgscan-disable-end: + ap_bgscan_disable_end: description: - - "End time, using a 24-hour clock in the format of hh:mm, for disabling background scanning (default = 00:00)." - ap-bgscan-disable-start: + - "End time, using a 24-hour clock in the format of hh:mm, for disabling background scanning ." + type: str + ap_bgscan_disable_start: description: - - "Start time, using a 24-hour clock in the format of hh:mm, for disabling background scanning (default = 00:00)." - ap-bgscan-duration: + - "Start time, using a 24-hour clock in the format of hh:mm, for disabling background scanning ." + type: str + ap_bgscan_duration: description: - - Listening time on a scanning channel (10 - 1000 msec, default = 20). - ap-bgscan-idle: + - Listening time on a scanning channel (10 - 1000 msec). + type: int + ap_bgscan_idle: description: - - Waiting time for channel inactivity before scanning this channel (0 - 1000 msec, default = 0). - ap-bgscan-intv: + - Waiting time for channel inactivity before scanning this channel (0 - 1000 msec). + type: int + ap_bgscan_intv: description: - - Period of time between scanning two channels (1 - 600 sec, default = 1). - ap-bgscan-period: + - Period of time between scanning two channels (1 - 600 sec). + type: int + ap_bgscan_period: description: - - Period of time between background scans (60 - 3600 sec, default = 600). - ap-bgscan-report-intv: + - Period of time between background scans (60 - 3600 sec). + type: int + ap_bgscan_report_intv: description: - - Period of time between background scan reports (15 - 600 sec, default = 30). - ap-fgscan-report-intv: + - Period of time between background scan reports (15 - 600 sec). + type: int + ap_fgscan_report_intv: description: - - Period of time between foreground scan reports (15 - 600 sec, default = 15). - ap-scan: + - Period of time between foreground scan reports (15 - 600 sec). + type: int + ap_scan: description: - Enable/disable rogue AP detection. + type: str choices: - disable - enable - ap-scan-passive: + ap_scan_passive: description: - - Enable/disable passive scanning. Enable means do not send probe request on any channels (default = disable). + - Enable/disable passive scanning. Enable means do not send probe request on any channels . + type: str choices: - enable - disable - asleap-attack: + asleap_attack: description: - - Enable/disable asleap attack detection (default = disable). + - Enable/disable asleap attack detection . + type: str choices: - enable - disable - assoc-flood-thresh: + assoc_flood_thresh: description: - The threshold value for association frame flooding. - assoc-flood-time: + type: int + assoc_flood_time: description: - Number of seconds after which a station is considered not connected. - assoc-frame-flood: + type: int + assoc_frame_flood: description: - - Enable/disable association frame flooding detection (default = disable). + - Enable/disable association frame flooding detection . + type: str choices: - enable - disable - auth-flood-thresh: + auth_flood_thresh: description: - The threshold value for authentication frame flooding. - auth-flood-time: + type: int + auth_flood_time: description: - Number of seconds after which a station is considered not connected. - auth-frame-flood: + type: int + auth_frame_flood: description: - - Enable/disable authentication frame flooding detection (default = disable). + - Enable/disable authentication frame flooding detection . + type: str choices: - enable - disable comment: description: - Comment. - deauth-broadcast: + type: str + deauth_broadcast: description: - - Enable/disable broadcasting de-authentication detection (default = disable). + - Enable/disable broadcasting de-authentication detection . + type: str choices: - enable - disable - deauth-unknown-src-thresh: + deauth_unknown_src_thresh: description: - "Threshold value per second to deauth unknown src for DoS attack (0: no limit)." - eapol-fail-flood: + type: int + eapol_fail_flood: description: - - Enable/disable EAPOL-Failure flooding (to AP) detection (default = disable). + - Enable/disable EAPOL-Failure flooding (to AP) detection . + type: str choices: - enable - disable - eapol-fail-intv: + eapol_fail_intv: description: - The detection interval for EAPOL-Failure flooding (1 - 3600 sec). - eapol-fail-thresh: + type: int + eapol_fail_thresh: description: - The threshold value for EAPOL-Failure flooding in specified interval. - eapol-logoff-flood: + type: int + eapol_logoff_flood: description: - - Enable/disable EAPOL-Logoff flooding (to AP) detection (default = disable). + - Enable/disable EAPOL-Logoff flooding (to AP) detection . + type: str choices: - enable - disable - eapol-logoff-intv: + eapol_logoff_intv: description: - The detection interval for EAPOL-Logoff flooding (1 - 3600 sec). - eapol-logoff-thresh: + type: int + eapol_logoff_thresh: description: - The threshold value for EAPOL-Logoff flooding in specified interval. - eapol-pre-fail-flood: + type: int + eapol_pre_fail_flood: description: - - Enable/disable premature EAPOL-Failure flooding (to STA) detection (default = disable). + - Enable/disable premature EAPOL-Failure flooding (to STA) detection . + type: str choices: - enable - disable - eapol-pre-fail-intv: + eapol_pre_fail_intv: description: - The detection interval for premature EAPOL-Failure flooding (1 - 3600 sec). - eapol-pre-fail-thresh: + type: int + eapol_pre_fail_thresh: description: - The threshold value for premature EAPOL-Failure flooding in specified interval. - eapol-pre-succ-flood: + type: int + eapol_pre_succ_flood: description: - - Enable/disable premature EAPOL-Success flooding (to STA) detection (default = disable). + - Enable/disable premature EAPOL-Success flooding (to STA) detection . + type: str choices: - enable - disable - eapol-pre-succ-intv: + eapol_pre_succ_intv: description: - The detection interval for premature EAPOL-Success flooding (1 - 3600 sec). - eapol-pre-succ-thresh: + type: int + eapol_pre_succ_thresh: description: - The threshold value for premature EAPOL-Success flooding in specified interval. - eapol-start-flood: + type: int + eapol_start_flood: description: - - Enable/disable EAPOL-Start flooding (to AP) detection (default = disable). + - Enable/disable EAPOL-Start flooding (to AP) detection . + type: str choices: - enable - disable - eapol-start-intv: + eapol_start_intv: description: - The detection interval for EAPOL-Start flooding (1 - 3600 sec). - eapol-start-thresh: + type: int + eapol_start_thresh: description: - The threshold value for EAPOL-Start flooding in specified interval. - eapol-succ-flood: + type: int + eapol_succ_flood: description: - - Enable/disable EAPOL-Success flooding (to AP) detection (default = disable). + - Enable/disable EAPOL-Success flooding (to AP) detection . + type: str choices: - enable - disable - eapol-succ-intv: + eapol_succ_intv: description: - The detection interval for EAPOL-Success flooding (1 - 3600 sec). - eapol-succ-thresh: + type: int + eapol_succ_thresh: description: - The threshold value for EAPOL-Success flooding in specified interval. - invalid-mac-oui: + type: int + invalid_mac_oui: description: - Enable/disable invalid MAC OUI detection. + type: str choices: - enable - disable - long-duration-attack: + long_duration_attack: description: - - Enable/disable long duration attack detection based on user configured threshold (default = disable). + - Enable/disable long duration attack detection based on user configured threshold . + type: str choices: - enable - disable - long-duration-thresh: + long_duration_thresh: description: - - Threshold value for long duration attack detection (1000 - 32767 usec, default = 8200). + - Threshold value for long duration attack detection (1000 - 32767 usec). + type: int name: description: - WIDS profile name. required: true - null-ssid-probe-resp: + type: str + null_ssid_probe_resp: description: - - Enable/disable null SSID probe response detection (default = disable). + - Enable/disable null SSID probe response detection . + type: str choices: - enable - disable - sensor-mode: + sensor_mode: description: - - Scan WiFi nearby stations (default = disable). + - Scan WiFi nearby stations . + type: str choices: - disable - foreign - both - spoofed-deauth: + spoofed_deauth: description: - - Enable/disable spoofed de-authentication attack detection (default = disable). + - Enable/disable spoofed de-authentication attack detection . + type: str choices: - enable - disable - weak-wep-iv: + weak_wep_iv: description: - - Enable/disable weak WEP IV (Initialization Vector) detection (default = disable). + - Enable/disable weak WEP IV (Initialization Vector) detection . + type: str choices: - enable - disable - wireless-bridge: + wireless_bridge: description: - - Enable/disable wireless bridge detection (default = disable). + - Enable/disable wireless bridge detection . + type: str choices: - enable - disable @@ -301,6 +363,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure wireless intrusion detection system (WIDS) profiles. fortios_wireless_controller_wids_profile: @@ -309,57 +372,57 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" wireless_controller_wids_profile: - state: "present" - ap-auto-suppress: "enable" - ap-bgscan-disable-day: "sunday" - ap-bgscan-disable-end: "" - ap-bgscan-disable-start: "" - ap-bgscan-duration: "7" - ap-bgscan-idle: "8" - ap-bgscan-intv: "9" - ap-bgscan-period: "10" - ap-bgscan-report-intv: "11" - ap-fgscan-report-intv: "12" - ap-scan: "disable" - ap-scan-passive: "enable" - asleap-attack: "enable" - assoc-flood-thresh: "16" - assoc-flood-time: "17" - assoc-frame-flood: "enable" - auth-flood-thresh: "19" - auth-flood-time: "20" - auth-frame-flood: "enable" + ap_auto_suppress: "enable" + ap_bgscan_disable_day: "sunday" + ap_bgscan_disable_end: "" + ap_bgscan_disable_start: "" + ap_bgscan_duration: "7" + ap_bgscan_idle: "8" + ap_bgscan_intv: "9" + ap_bgscan_period: "10" + ap_bgscan_report_intv: "11" + ap_fgscan_report_intv: "12" + ap_scan: "disable" + ap_scan_passive: "enable" + asleap_attack: "enable" + assoc_flood_thresh: "16" + assoc_flood_time: "17" + assoc_frame_flood: "enable" + auth_flood_thresh: "19" + auth_flood_time: "20" + auth_frame_flood: "enable" comment: "Comment." - deauth-broadcast: "enable" - deauth-unknown-src-thresh: "24" - eapol-fail-flood: "enable" - eapol-fail-intv: "26" - eapol-fail-thresh: "27" - eapol-logoff-flood: "enable" - eapol-logoff-intv: "29" - eapol-logoff-thresh: "30" - eapol-pre-fail-flood: "enable" - eapol-pre-fail-intv: "32" - eapol-pre-fail-thresh: "33" - eapol-pre-succ-flood: "enable" - eapol-pre-succ-intv: "35" - eapol-pre-succ-thresh: "36" - eapol-start-flood: "enable" - eapol-start-intv: "38" - eapol-start-thresh: "39" - eapol-succ-flood: "enable" - eapol-succ-intv: "41" - eapol-succ-thresh: "42" - invalid-mac-oui: "enable" - long-duration-attack: "enable" - long-duration-thresh: "45" + deauth_broadcast: "enable" + deauth_unknown_src_thresh: "24" + eapol_fail_flood: "enable" + eapol_fail_intv: "26" + eapol_fail_thresh: "27" + eapol_logoff_flood: "enable" + eapol_logoff_intv: "29" + eapol_logoff_thresh: "30" + eapol_pre_fail_flood: "enable" + eapol_pre_fail_intv: "32" + eapol_pre_fail_thresh: "33" + eapol_pre_succ_flood: "enable" + eapol_pre_succ_intv: "35" + eapol_pre_succ_thresh: "36" + eapol_start_flood: "enable" + eapol_start_intv: "38" + eapol_start_thresh: "39" + eapol_succ_flood: "enable" + eapol_succ_intv: "41" + eapol_succ_thresh: "42" + invalid_mac_oui: "enable" + long_duration_attack: "enable" + long_duration_thresh: "45" name: "default_name_46" - null-ssid-probe-resp: "enable" - sensor-mode: "disable" - spoofed-deauth: "enable" - weak-wep-iv: "enable" - wireless-bridge: "enable" + null_ssid_probe_resp: "enable" + sensor_mode: "disable" + spoofed_deauth: "enable" + weak_wep_iv: "enable" + wireless_bridge: "enable" ''' RETURN = ''' @@ -422,12 +485,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 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']: @@ -435,27 +502,27 @@ def login(data, fos): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_wireless_controller_wids_profile_data(json): - option_list = ['ap-auto-suppress', 'ap-bgscan-disable-day', 'ap-bgscan-disable-end', - 'ap-bgscan-disable-start', 'ap-bgscan-duration', 'ap-bgscan-idle', - 'ap-bgscan-intv', 'ap-bgscan-period', 'ap-bgscan-report-intv', - 'ap-fgscan-report-intv', 'ap-scan', 'ap-scan-passive', - 'asleap-attack', 'assoc-flood-thresh', 'assoc-flood-time', - 'assoc-frame-flood', 'auth-flood-thresh', 'auth-flood-time', - 'auth-frame-flood', 'comment', 'deauth-broadcast', - 'deauth-unknown-src-thresh', 'eapol-fail-flood', 'eapol-fail-intv', - 'eapol-fail-thresh', 'eapol-logoff-flood', 'eapol-logoff-intv', - 'eapol-logoff-thresh', 'eapol-pre-fail-flood', 'eapol-pre-fail-intv', - 'eapol-pre-fail-thresh', 'eapol-pre-succ-flood', 'eapol-pre-succ-intv', - 'eapol-pre-succ-thresh', 'eapol-start-flood', 'eapol-start-intv', - 'eapol-start-thresh', 'eapol-succ-flood', 'eapol-succ-intv', - 'eapol-succ-thresh', 'invalid-mac-oui', 'long-duration-attack', - 'long-duration-thresh', 'name', 'null-ssid-probe-resp', - 'sensor-mode', 'spoofed-deauth', 'weak-wep-iv', - 'wireless-bridge'] + option_list = ['ap_auto_suppress', 'ap_bgscan_disable_day', 'ap_bgscan_disable_end', + 'ap_bgscan_disable_start', 'ap_bgscan_duration', 'ap_bgscan_idle', + 'ap_bgscan_intv', 'ap_bgscan_period', 'ap_bgscan_report_intv', + 'ap_fgscan_report_intv', 'ap_scan', 'ap_scan_passive', + 'asleap_attack', 'assoc_flood_thresh', 'assoc_flood_time', + 'assoc_frame_flood', 'auth_flood_thresh', 'auth_flood_time', + 'auth_frame_flood', 'comment', 'deauth_broadcast', + 'deauth_unknown_src_thresh', 'eapol_fail_flood', 'eapol_fail_intv', + 'eapol_fail_thresh', 'eapol_logoff_flood', 'eapol_logoff_intv', + 'eapol_logoff_thresh', 'eapol_pre_fail_flood', 'eapol_pre_fail_intv', + 'eapol_pre_fail_thresh', 'eapol_pre_succ_flood', 'eapol_pre_succ_intv', + 'eapol_pre_succ_thresh', 'eapol_start_flood', 'eapol_start_intv', + 'eapol_start_thresh', 'eapol_succ_flood', 'eapol_succ_intv', + 'eapol_succ_thresh', 'invalid_mac_oui', 'long_duration_attack', + 'long_duration_thresh', 'name', 'null_ssid_probe_resp', + 'sensor_mode', 'spoofed_deauth', 'weak_wep_iv', + 'wireless_bridge'] dictionary = {} for attribute in option_list: @@ -465,132 +532,137 @@ def filter_wireless_controller_wids_profile_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +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 wireless_controller_wids_profile(data, fos): vdom = data['vdom'] + state = data['state'] wireless_controller_wids_profile_data = data['wireless_controller_wids_profile'] - flattened_data = flatten_multilists_attributes(wireless_controller_wids_profile_data) - filtered_data = filter_wireless_controller_wids_profile_data(flattened_data) - if wireless_controller_wids_profile_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_wireless_controller_wids_profile_data(wireless_controller_wids_profile_data)) + + if state == "present": return fos.set('wireless-controller', 'wids-profile', data=filtered_data, vdom=vdom) - elif wireless_controller_wids_profile_data['state'] == "absent": + elif state == "absent": return fos.delete('wireless-controller', 'wids-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_wireless_controller(data, fos): - login(data, fos) if data['wireless_controller_wids_profile']: resp = wireless_controller_wids_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"]}, "wireless_controller_wids_profile": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, - "ap-auto-suppress": {"required": False, "type": "str", + "ap_auto_suppress": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ap-bgscan-disable-day": {"required": False, "type": "str", + "ap_bgscan_disable_day": {"required": False, "type": "str", "choices": ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"]}, - "ap-bgscan-disable-end": {"required": False, "type": "str"}, - "ap-bgscan-disable-start": {"required": False, "type": "str"}, - "ap-bgscan-duration": {"required": False, "type": "int"}, - "ap-bgscan-idle": {"required": False, "type": "int"}, - "ap-bgscan-intv": {"required": False, "type": "int"}, - "ap-bgscan-period": {"required": False, "type": "int"}, - "ap-bgscan-report-intv": {"required": False, "type": "int"}, - "ap-fgscan-report-intv": {"required": False, "type": "int"}, - "ap-scan": {"required": False, "type": "str", + "ap_bgscan_disable_end": {"required": False, "type": "str"}, + "ap_bgscan_disable_start": {"required": False, "type": "str"}, + "ap_bgscan_duration": {"required": False, "type": "int"}, + "ap_bgscan_idle": {"required": False, "type": "int"}, + "ap_bgscan_intv": {"required": False, "type": "int"}, + "ap_bgscan_period": {"required": False, "type": "int"}, + "ap_bgscan_report_intv": {"required": False, "type": "int"}, + "ap_fgscan_report_intv": {"required": False, "type": "int"}, + "ap_scan": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "ap-scan-passive": {"required": False, "type": "str", + "ap_scan_passive": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "asleap-attack": {"required": False, "type": "str", + "asleap_attack": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "assoc-flood-thresh": {"required": False, "type": "int"}, - "assoc-flood-time": {"required": False, "type": "int"}, - "assoc-frame-flood": {"required": False, "type": "str", + "assoc_flood_thresh": {"required": False, "type": "int"}, + "assoc_flood_time": {"required": False, "type": "int"}, + "assoc_frame_flood": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "auth-flood-thresh": {"required": False, "type": "int"}, - "auth-flood-time": {"required": False, "type": "int"}, - "auth-frame-flood": {"required": False, "type": "str", + "auth_flood_thresh": {"required": False, "type": "int"}, + "auth_flood_time": {"required": False, "type": "int"}, + "auth_frame_flood": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "comment": {"required": False, "type": "str"}, - "deauth-broadcast": {"required": False, "type": "str", + "deauth_broadcast": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "deauth-unknown-src-thresh": {"required": False, "type": "int"}, - "eapol-fail-flood": {"required": False, "type": "str", + "deauth_unknown_src_thresh": {"required": False, "type": "int"}, + "eapol_fail_flood": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "eapol-fail-intv": {"required": False, "type": "int"}, - "eapol-fail-thresh": {"required": False, "type": "int"}, - "eapol-logoff-flood": {"required": False, "type": "str", + "eapol_fail_intv": {"required": False, "type": "int"}, + "eapol_fail_thresh": {"required": False, "type": "int"}, + "eapol_logoff_flood": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "eapol-logoff-intv": {"required": False, "type": "int"}, - "eapol-logoff-thresh": {"required": False, "type": "int"}, - "eapol-pre-fail-flood": {"required": False, "type": "str", + "eapol_logoff_intv": {"required": False, "type": "int"}, + "eapol_logoff_thresh": {"required": False, "type": "int"}, + "eapol_pre_fail_flood": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "eapol-pre-fail-intv": {"required": False, "type": "int"}, - "eapol-pre-fail-thresh": {"required": False, "type": "int"}, - "eapol-pre-succ-flood": {"required": False, "type": "str", + "eapol_pre_fail_intv": {"required": False, "type": "int"}, + "eapol_pre_fail_thresh": {"required": False, "type": "int"}, + "eapol_pre_succ_flood": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "eapol-pre-succ-intv": {"required": False, "type": "int"}, - "eapol-pre-succ-thresh": {"required": False, "type": "int"}, - "eapol-start-flood": {"required": False, "type": "str", + "eapol_pre_succ_intv": {"required": False, "type": "int"}, + "eapol_pre_succ_thresh": {"required": False, "type": "int"}, + "eapol_start_flood": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "eapol-start-intv": {"required": False, "type": "int"}, - "eapol-start-thresh": {"required": False, "type": "int"}, - "eapol-succ-flood": {"required": False, "type": "str", + "eapol_start_intv": {"required": False, "type": "int"}, + "eapol_start_thresh": {"required": False, "type": "int"}, + "eapol_succ_flood": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "eapol-succ-intv": {"required": False, "type": "int"}, - "eapol-succ-thresh": {"required": False, "type": "int"}, - "invalid-mac-oui": {"required": False, "type": "str", + "eapol_succ_intv": {"required": False, "type": "int"}, + "eapol_succ_thresh": {"required": False, "type": "int"}, + "invalid_mac_oui": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "long-duration-attack": {"required": False, "type": "str", + "long_duration_attack": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "long-duration-thresh": {"required": False, "type": "int"}, + "long_duration_thresh": {"required": False, "type": "int"}, "name": {"required": True, "type": "str"}, - "null-ssid-probe-resp": {"required": False, "type": "str", + "null_ssid_probe_resp": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "sensor-mode": {"required": False, "type": "str", + "sensor_mode": {"required": False, "type": "str", "choices": ["disable", "foreign", "both"]}, - "spoofed-deauth": {"required": False, "type": "str", + "spoofed_deauth": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "weak-wep-iv": {"required": False, "type": "str", + "weak_wep_iv": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "wireless-bridge": {"required": False, "type": "str", + "wireless_bridge": {"required": False, "type": "str", "choices": ["enable", "disable"]} } @@ -599,14 +671,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") - 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_wireless_controller(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_wireless_controller(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_wireless_controller(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_wireless_controller_wtp.py b/lib/ansible/modules/network/fortios/fortios_wireless_controller_wtp.py index 6844efa8c2a..e19fed38703 100644 --- a/lib/ansible/modules/network/fortios/fortios_wireless_controller_wtp.py +++ b/lib/ansible/modules/network/fortios/fortios_wireless_controller_wtp.py @@ -26,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_wireless_controller_wtp short_description: Configure Wireless Termination Points (WTPs), that is, FortiAPs or APs to be managed by FortiGate in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify wireless_controller feature and wtp category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -41,43 +41,57 @@ 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: 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 wireless_controller_wtp: description: - Configure Wireless Termination Points (WTPs), that is, FortiAPs or APs to be managed by FortiGate. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent admin: description: - Configure how the FortiGate operating as a wireless controller discovers and manages this WTP, AP or FortiAP. + type: str choices: - discovered - disable @@ -85,173 +99,207 @@ options: allowaccess: description: - Control management access to the managed WTP, FortiAP, or AP. Separate entries with a space. + type: str choices: - telnet - http - https - ssh - bonjour-profile: + bonjour_profile: description: - Bonjour profile name. Source wireless-controller.bonjour-profile.name. - coordinate-enable: + type: str + coordinate_enable: description: - Enable/disable WTP coordinates (X,Y axis). + type: str choices: - enable - disable - coordinate-latitude: + coordinate_latitude: description: - WTP latitude coordinate. - coordinate-longitude: + type: str + coordinate_longitude: description: - WTP longitude coordinate. - coordinate-x: + type: str + coordinate_x: description: - X axis coordinate. - coordinate-y: + type: str + coordinate_y: description: - Y axis coordinate. - image-download: + type: str + image_download: description: - Enable/disable WTP image download. + type: str choices: - enable - disable index: description: - Index (0 - 4294967295). - ip-fragment-preventing: + type: int + ip_fragment_preventing: description: - - Method by which IP fragmentation is prevented for CAPWAP tunneled control and data packets (default = tcp-mss-adjust). + - Method by which IP fragmentation is prevented for CAPWAP tunneled control and data packets . + type: str choices: - tcp-mss-adjust - icmp-unreachable lan: description: - WTP LAN port mapping. + type: dict suboptions: - port-mode: + port_mode: description: - LAN port mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port-ssid: + port_ssid: description: - Bridge LAN port to SSID. Source wireless-controller.vap.name. - port1-mode: + type: str + port1_mode: description: - LAN port 1 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port1-ssid: + port1_ssid: description: - Bridge LAN port 1 to SSID. Source wireless-controller.vap.name. - port2-mode: + type: str + port2_mode: description: - LAN port 2 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port2-ssid: + port2_ssid: description: - Bridge LAN port 2 to SSID. Source wireless-controller.vap.name. - port3-mode: + type: str + port3_mode: description: - LAN port 3 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port3-ssid: + port3_ssid: description: - Bridge LAN port 3 to SSID. Source wireless-controller.vap.name. - port4-mode: + type: str + port4_mode: description: - LAN port 4 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port4-ssid: + port4_ssid: description: - Bridge LAN port 4 to SSID. Source wireless-controller.vap.name. - port5-mode: + type: str + port5_mode: description: - LAN port 5 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port5-ssid: + port5_ssid: description: - Bridge LAN port 5 to SSID. Source wireless-controller.vap.name. - port6-mode: + type: str + port6_mode: description: - LAN port 6 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port6-ssid: + port6_ssid: description: - Bridge LAN port 6 to SSID. Source wireless-controller.vap.name. - port7-mode: + type: str + port7_mode: description: - LAN port 7 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port7-ssid: + port7_ssid: description: - Bridge LAN port 7 to SSID. Source wireless-controller.vap.name. - port8-mode: + type: str + port8_mode: description: - LAN port 8 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port8-ssid: + port8_ssid: description: - Bridge LAN port 8 to SSID. Source wireless-controller.vap.name. - led-state: + type: str + led_state: description: - Enable to allow the FortiAPs LEDs to light. Disable to keep the LEDs off. You may want to keep the LEDs off so they are not distracting in low light areas etc. + type: str choices: - enable - disable location: description: - Field for describing the physical location of the WTP, AP or FortiAP. - login-passwd: + type: str + login_passwd: description: - Set the managed WTP, FortiAP, or AP's administrator password. - login-passwd-change: + type: str + login_passwd_change: description: - - Change or reset the administrator password of a managed WTP, FortiAP or AP (yes, default, or no, default = no). + - Change or reset the administrator password of a managed WTP, FortiAP or AP (yes, default, or no). + type: str choices: - yes - default - no - mesh-bridge-enable: + mesh_bridge_enable: description: - Enable/disable mesh Ethernet bridge when WTP is configured as a mesh branch/leaf AP. + type: str choices: - default - enable @@ -259,69 +307,82 @@ options: name: description: - WTP, AP or FortiAP configuration name. - override-allowaccess: + type: str + override_allowaccess: description: - Enable to override the WTP profile management access configuration. + type: str choices: - enable - disable - override-ip-fragment: + override_ip_fragment: description: - Enable/disable overriding the WTP profile IP fragment prevention setting. + type: str choices: - enable - disable - override-lan: + override_lan: description: - Enable to override the WTP profile LAN port setting. + type: str choices: - enable - disable - override-led-state: + override_led_state: description: - Enable to override the profile LED state setting for this FortiAP. You must enable this option to use the led-state command to turn off the FortiAP's LEDs. + type: str choices: - enable - disable - override-login-passwd-change: + override_login_passwd_change: description: - Enable to override the WTP profile login-password (administrator password) setting. + type: str choices: - enable - disable - override-split-tunnel: + override_split_tunnel: description: - Enable/disable overriding the WTP profile split tunneling setting. + type: str choices: - enable - disable - override-wan-port-mode: + override_wan_port_mode: description: - Enable/disable overriding the wan-port-mode in the WTP profile. + type: str choices: - enable - disable - radio-1: + radio_1: description: - Configuration options for radio 1. + type: dict suboptions: - auto-power-high: + auto_power_high: description: - Automatic transmission power high limit in decibels (dB) of the measured power referenced to one milliwatt (mW), or dBm (10 - 17 - dBm, default = 17). - auto-power-level: + dBm). + type: int + auto_power_level: description: - - Enable/disable automatic power-level adjustment to prevent co-channel interference (default = enable). + - Enable/disable automatic power-level adjustment to prevent co-channel interference . + type: str choices: - enable - disable - auto-power-low: + auto_power_low: description: - Automatic transmission power low limit in dBm (the actual range of transmit power depends on the AP platform type). + type: int band: description: - WiFi band that Radio 1 operates on. + type: str choices: - 802.11a - 802.11b @@ -338,87 +399,105 @@ options: channel: description: - Selected list of wireless radio channels. + type: list suboptions: chan: description: - Channel number. required: true - override-analysis: + type: str + override_analysis: description: - Enable to override the WTP profile spectrum analysis configuration. + type: str choices: - enable - disable - override-band: + override_band: description: - Enable to override the WTP profile band setting. + type: str choices: - enable - disable - override-channel: + override_channel: description: - Enable to override WTP profile channel settings. + type: str choices: - enable - disable - override-txpower: + override_txpower: description: - Enable to override the WTP profile power level configuration. + type: str choices: - enable - disable - override-vaps: + override_vaps: description: - Enable to override WTP profile Virtual Access Point (VAP) settings. + type: str choices: - enable - disable - power-level: + power_level: description: - - Radio power level as a percentage of the maximum transmit power (0 - 100, default = 100). - radio-id: + - Radio power level as a percentage of the maximum transmit power (0 - 100). + type: int + radio_id: description: - radio-id - spectrum-analysis: + type: int + spectrum_analysis: description: - Enable/disable spectrum analysis to find interference that would negatively impact wireless performance. + type: str choices: - enable - disable - vap-all: + vap_all: description: - - Enable/disable the automatic inheritance of all Virtual Access Points (VAPs) (default = enable). + - Enable/disable the automatic inheritance of all Virtual Access Points (VAPs) . + type: str choices: - enable - disable vaps: description: - Manually selected list of Virtual Access Points (VAPs). + type: list suboptions: name: description: - Virtual Access Point (VAP) name. Source wireless-controller.vap-group.name wireless-controller.vap.name. required: true - radio-2: + type: str + radio_2: description: - Configuration options for radio 2. + type: dict suboptions: - auto-power-high: + auto_power_high: description: - Automatic transmission power high limit in decibels (dB) of the measured power referenced to one milliwatt (mW), or dBm (10 - 17 - dBm, default = 17). - auto-power-level: + dBm). + type: int + auto_power_level: description: - - Enable/disable automatic power-level adjustment to prevent co-channel interference (default = enable). + - Enable/disable automatic power-level adjustment to prevent co-channel interference . + type: str choices: - enable - disable - auto-power-low: + auto_power_low: description: - Automatic transmission power low limit in dBm (the actual range of transmit power depends on the AP platform type). + type: int band: description: - WiFi band that Radio 1 operates on. + type: str choices: - 802.11a - 802.11b @@ -435,116 +514,139 @@ options: channel: description: - Selected list of wireless radio channels. + type: list suboptions: chan: description: - Channel number. required: true - override-analysis: + type: str + override_analysis: description: - Enable to override the WTP profile spectrum analysis configuration. + type: str choices: - enable - disable - override-band: + override_band: description: - Enable to override the WTP profile band setting. + type: str choices: - enable - disable - override-channel: + override_channel: description: - Enable to override WTP profile channel settings. + type: str choices: - enable - disable - override-txpower: + override_txpower: description: - Enable to override the WTP profile power level configuration. + type: str choices: - enable - disable - override-vaps: + override_vaps: description: - Enable to override WTP profile Virtual Access Point (VAP) settings. + type: str choices: - enable - disable - power-level: + power_level: description: - - Radio power level as a percentage of the maximum transmit power (0 - 100, default = 100). - radio-id: + - Radio power level as a percentage of the maximum transmit power (0 - 100). + type: int + radio_id: description: - radio-id - spectrum-analysis: + type: int + spectrum_analysis: description: - Enable/disable spectrum analysis to find interference that would negatively impact wireless performance. + type: str choices: - enable - disable - vap-all: + vap_all: description: - - Enable/disable the automatic inheritance of all Virtual Access Points (VAPs) (default = enable). + - Enable/disable the automatic inheritance of all Virtual Access Points (VAPs) . + type: str choices: - enable - disable vaps: description: - Manually selected list of Virtual Access Points (VAPs). + type: list suboptions: name: description: - Virtual Access Point (VAP) name. Source wireless-controller.vap-group.name wireless-controller.vap.name. required: true - split-tunneling-acl: + type: str + split_tunneling_acl: description: - Split tunneling ACL filter list. + type: list suboptions: - dest-ip: + dest_ip: description: - Destination IP and mask for the split-tunneling subnet. + type: str id: description: - ID. required: true - split-tunneling-acl-local-ap-subnet: + type: int + split_tunneling_acl_local_ap_subnet: description: - - Enable/disable automatically adding local subnetwork of FortiAP to split-tunneling ACL (default = disable). + - Enable/disable automatically adding local subnetwork of FortiAP to split-tunneling ACL . + type: str choices: - enable - disable - split-tunneling-acl-path: + split_tunneling_acl_path: description: - Split tunneling ACL path is local/tunnel. + type: str choices: - tunnel - local - tun-mtu-downlink: + tun_mtu_downlink: description: - Downlink tunnel MTU in octets. Set the value to either 0 (by default), 576, or 1500. - tun-mtu-uplink: + type: int + tun_mtu_uplink: description: - Uplink tunnel maximum transmission unit (MTU) in octets (eight-bit bytes). Set the value to either 0 (by default), 576, or 1500. - wan-port-mode: + type: int + wan_port_mode: description: - Enable/disable using the FortiAP WAN port as a LAN port. + type: str choices: - wan-lan - wan-only - wtp-id: + wtp_id: description: - WTP ID. - required: true - wtp-mode: + type: str + wtp_mode: description: - WTP, AP, or FortiAP operating mode; normal (by default) or remote. A tunnel mode SSID can be assigned to an AP in normal mode but not remote mode, while a local-bridge mode SSID can be assigned to an AP in either normal mode or remote mode. + type: str choices: - normal - remote - wtp-profile: + wtp_profile: description: - WTP profile name to apply to this WTP, AP or FortiAP. Source wireless-controller.wtp-profile.name. + type: str ''' EXAMPLES = ''' @@ -554,6 +656,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure Wireless Termination Points (WTPs), that is, FortiAPs or APs to be managed by FortiGate. fortios_wireless_controller_wtp: @@ -562,103 +665,103 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" wireless_controller_wtp: - state: "present" admin: "discovered" allowaccess: "telnet" - bonjour-profile: " (source wireless-controller.bonjour-profile.name)" - coordinate-enable: "enable" - coordinate-latitude: "" - coordinate-longitude: "" - coordinate-x: "" - coordinate-y: "" - image-download: "enable" + bonjour_profile: " (source wireless-controller.bonjour-profile.name)" + coordinate_enable: "enable" + coordinate_latitude: "" + coordinate_longitude: "" + coordinate_x: "" + coordinate_y: "" + image_download: "enable" index: "12" - ip-fragment-preventing: "tcp-mss-adjust" + ip_fragment_preventing: "tcp-mss-adjust" lan: - port-mode: "offline" - port-ssid: " (source wireless-controller.vap.name)" - port1-mode: "offline" - port1-ssid: " (source wireless-controller.vap.name)" - port2-mode: "offline" - port2-ssid: " (source wireless-controller.vap.name)" - port3-mode: "offline" - port3-ssid: " (source wireless-controller.vap.name)" - port4-mode: "offline" - port4-ssid: " (source wireless-controller.vap.name)" - port5-mode: "offline" - port5-ssid: " (source wireless-controller.vap.name)" - port6-mode: "offline" - port6-ssid: " (source wireless-controller.vap.name)" - port7-mode: "offline" - port7-ssid: " (source wireless-controller.vap.name)" - port8-mode: "offline" - port8-ssid: " (source wireless-controller.vap.name)" - led-state: "enable" + port_mode: "offline" + port_ssid: " (source wireless-controller.vap.name)" + port1_mode: "offline" + port1_ssid: " (source wireless-controller.vap.name)" + port2_mode: "offline" + port2_ssid: " (source wireless-controller.vap.name)" + port3_mode: "offline" + port3_ssid: " (source wireless-controller.vap.name)" + port4_mode: "offline" + port4_ssid: " (source wireless-controller.vap.name)" + port5_mode: "offline" + port5_ssid: " (source wireless-controller.vap.name)" + port6_mode: "offline" + port6_ssid: " (source wireless-controller.vap.name)" + port7_mode: "offline" + port7_ssid: " (source wireless-controller.vap.name)" + port8_mode: "offline" + port8_ssid: " (source wireless-controller.vap.name)" + led_state: "enable" location: "" - login-passwd: "" - login-passwd-change: "yes" - mesh-bridge-enable: "default" + login_passwd: "" + login_passwd_change: "yes" + mesh_bridge_enable: "default" name: "default_name_38" - override-allowaccess: "enable" - override-ip-fragment: "enable" - override-lan: "enable" - override-led-state: "enable" - override-login-passwd-change: "enable" - override-split-tunnel: "enable" - override-wan-port-mode: "enable" - radio-1: - auto-power-high: "47" - auto-power-level: "enable" - auto-power-low: "49" + override_allowaccess: "enable" + override_ip_fragment: "enable" + override_lan: "enable" + override_led_state: "enable" + override_login_passwd_change: "enable" + override_split_tunnel: "enable" + override_wan_port_mode: "enable" + radio_1: + auto_power_high: "47" + auto_power_level: "enable" + auto_power_low: "49" band: "802.11a" channel: - chan: "" - override-analysis: "enable" - override-band: "enable" - override-channel: "enable" - override-txpower: "enable" - override-vaps: "enable" - power-level: "58" - radio-id: "59" - spectrum-analysis: "enable" - vap-all: "enable" + override_analysis: "enable" + override_band: "enable" + override_channel: "enable" + override_txpower: "enable" + override_vaps: "enable" + power_level: "58" + radio_id: "59" + spectrum_analysis: "enable" + vap_all: "enable" vaps: - name: "default_name_63 (source wireless-controller.vap-group.name wireless-controller.vap.name)" - radio-2: - auto-power-high: "65" - auto-power-level: "enable" - auto-power-low: "67" + radio_2: + auto_power_high: "65" + auto_power_level: "enable" + auto_power_low: "67" band: "802.11a" channel: - chan: "" - override-analysis: "enable" - override-band: "enable" - override-channel: "enable" - override-txpower: "enable" - override-vaps: "enable" - power-level: "76" - radio-id: "77" - spectrum-analysis: "enable" - vap-all: "enable" + override_analysis: "enable" + override_band: "enable" + override_channel: "enable" + override_txpower: "enable" + override_vaps: "enable" + power_level: "76" + radio_id: "77" + spectrum_analysis: "enable" + vap_all: "enable" vaps: - name: "default_name_81 (source wireless-controller.vap-group.name wireless-controller.vap.name)" - split-tunneling-acl: + split_tunneling_acl: - - dest-ip: "" + dest_ip: "" id: "84" - split-tunneling-acl-local-ap-subnet: "enable" - split-tunneling-acl-path: "tunnel" - tun-mtu-downlink: "87" - tun-mtu-uplink: "88" - wan-port-mode: "wan-lan" - wtp-id: "" - wtp-mode: "normal" - wtp-profile: " (source wireless-controller.wtp-profile.name)" + split_tunneling_acl_local_ap_subnet: "enable" + split_tunneling_acl_path: "tunnel" + tun_mtu_downlink: "87" + tun_mtu_uplink: "88" + wan_port_mode: "wan-lan" + wtp_id: "" + wtp_mode: "normal" + wtp_profile: " (source wireless-controller.wtp-profile.name)" ''' RETURN = ''' @@ -721,12 +824,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 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']: @@ -734,22 +841,22 @@ def login(data, fos): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_wireless_controller_wtp_data(json): - option_list = ['admin', 'allowaccess', 'bonjour-profile', - 'coordinate-enable', 'coordinate-latitude', 'coordinate-longitude', - 'coordinate-x', 'coordinate-y', 'image-download', - 'index', 'ip-fragment-preventing', 'lan', - 'led-state', 'location', 'login-passwd', - 'login-passwd-change', 'mesh-bridge-enable', 'name', - 'override-allowaccess', 'override-ip-fragment', 'override-lan', - 'override-led-state', 'override-login-passwd-change', 'override-split-tunnel', - 'override-wan-port-mode', 'radio-1', 'radio-2', - 'split-tunneling-acl', 'split-tunneling-acl-local-ap-subnet', 'split-tunneling-acl-path', - 'tun-mtu-downlink', 'tun-mtu-uplink', 'wan-port-mode', - 'wtp-id', 'wtp-mode', 'wtp-profile'] + option_list = ['admin', 'allowaccess', 'bonjour_profile', + 'coordinate_enable', 'coordinate_latitude', 'coordinate_longitude', + 'coordinate_x', 'coordinate_y', 'image_download', + 'index', 'ip_fragment_preventing', 'lan', + 'led_state', 'location', 'login_passwd', + 'login_passwd_change', 'mesh_bridge_enable', 'name', + 'override_allowaccess', 'override_ip_fragment', 'override_lan', + 'override_led_state', 'override_login_passwd_change', 'override_split_tunnel', + 'override_wan_port_mode', 'radio_1', 'radio_2', + 'split_tunneling_acl', 'split_tunneling_acl_local_ap_subnet', 'split_tunneling_acl_path', + 'tun_mtu_downlink', 'tun_mtu_uplink', 'wan_port_mode', + 'wtp_id', 'wtp_mode', 'wtp_profile'] dictionary = {} for attribute in option_list: @@ -759,146 +866,151 @@ def filter_wireless_controller_wtp_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +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 wireless_controller_wtp(data, fos): vdom = data['vdom'] + state = data['state'] wireless_controller_wtp_data = data['wireless_controller_wtp'] - flattened_data = flatten_multilists_attributes(wireless_controller_wtp_data) - filtered_data = filter_wireless_controller_wtp_data(flattened_data) - if wireless_controller_wtp_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_wireless_controller_wtp_data(wireless_controller_wtp_data)) + + if state == "present": return fos.set('wireless-controller', 'wtp', data=filtered_data, vdom=vdom) - elif wireless_controller_wtp_data['state'] == "absent": + elif state == "absent": return fos.delete('wireless-controller', 'wtp', mkey=filtered_data['wtp-id'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_wireless_controller(data, fos): - login(data, fos) if data['wireless_controller_wtp']: resp = wireless_controller_wtp(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"]}, "wireless_controller_wtp": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "admin": {"required": False, "type": "str", "choices": ["discovered", "disable", "enable"]}, "allowaccess": {"required": False, "type": "str", "choices": ["telnet", "http", "https", "ssh"]}, - "bonjour-profile": {"required": False, "type": "str"}, - "coordinate-enable": {"required": False, "type": "str", + "bonjour_profile": {"required": False, "type": "str"}, + "coordinate_enable": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "coordinate-latitude": {"required": False, "type": "str"}, - "coordinate-longitude": {"required": False, "type": "str"}, - "coordinate-x": {"required": False, "type": "str"}, - "coordinate-y": {"required": False, "type": "str"}, - "image-download": {"required": False, "type": "str", + "coordinate_latitude": {"required": False, "type": "str"}, + "coordinate_longitude": {"required": False, "type": "str"}, + "coordinate_x": {"required": False, "type": "str"}, + "coordinate_y": {"required": False, "type": "str"}, + "image_download": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "index": {"required": False, "type": "int"}, - "ip-fragment-preventing": {"required": False, "type": "str", + "ip_fragment_preventing": {"required": False, "type": "str", "choices": ["tcp-mss-adjust", "icmp-unreachable"]}, "lan": {"required": False, "type": "dict", "options": { - "port-mode": {"required": False, "type": "str", + "port_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port-ssid": {"required": False, "type": "str"}, - "port1-mode": {"required": False, "type": "str", + "port_ssid": {"required": False, "type": "str"}, + "port1_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port1-ssid": {"required": False, "type": "str"}, - "port2-mode": {"required": False, "type": "str", + "port1_ssid": {"required": False, "type": "str"}, + "port2_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port2-ssid": {"required": False, "type": "str"}, - "port3-mode": {"required": False, "type": "str", + "port2_ssid": {"required": False, "type": "str"}, + "port3_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port3-ssid": {"required": False, "type": "str"}, - "port4-mode": {"required": False, "type": "str", + "port3_ssid": {"required": False, "type": "str"}, + "port4_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port4-ssid": {"required": False, "type": "str"}, - "port5-mode": {"required": False, "type": "str", + "port4_ssid": {"required": False, "type": "str"}, + "port5_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port5-ssid": {"required": False, "type": "str"}, - "port6-mode": {"required": False, "type": "str", + "port5_ssid": {"required": False, "type": "str"}, + "port6_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port6-ssid": {"required": False, "type": "str"}, - "port7-mode": {"required": False, "type": "str", + "port6_ssid": {"required": False, "type": "str"}, + "port7_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port7-ssid": {"required": False, "type": "str"}, - "port8-mode": {"required": False, "type": "str", + "port7_ssid": {"required": False, "type": "str"}, + "port8_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port8-ssid": {"required": False, "type": "str"} + "port8_ssid": {"required": False, "type": "str"} }}, - "led-state": {"required": False, "type": "str", + "led_state": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "location": {"required": False, "type": "str"}, - "login-passwd": {"required": False, "type": "str"}, - "login-passwd-change": {"required": False, "type": "str", + "login_passwd": {"required": False, "type": "str"}, + "login_passwd_change": {"required": False, "type": "str", "choices": ["yes", "default", "no"]}, - "mesh-bridge-enable": {"required": False, "type": "str", + "mesh_bridge_enable": {"required": False, "type": "str", "choices": ["default", "enable", "disable"]}, "name": {"required": False, "type": "str"}, - "override-allowaccess": {"required": False, "type": "str", + "override_allowaccess": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-ip-fragment": {"required": False, "type": "str", + "override_ip_fragment": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-lan": {"required": False, "type": "str", + "override_lan": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-led-state": {"required": False, "type": "str", + "override_led_state": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-login-passwd-change": {"required": False, "type": "str", + "override_login_passwd_change": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-split-tunnel": {"required": False, "type": "str", + "override_split_tunnel": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-wan-port-mode": {"required": False, "type": "str", + "override_wan_port_mode": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "radio-1": {"required": False, "type": "dict", + "radio_1": {"required": False, "type": "dict", "options": { - "auto-power-high": {"required": False, "type": "int"}, - "auto-power-level": {"required": False, "type": "str", + "auto_power_high": {"required": False, "type": "int"}, + "auto_power_level": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "auto-power-low": {"required": False, "type": "int"}, + "auto_power_low": {"required": False, "type": "int"}, "band": {"required": False, "type": "str", "choices": ["802.11a", "802.11b", "802.11g", "802.11n", "802.11n-5G", "802.11n,g-only", @@ -908,33 +1020,33 @@ def main(): "options": { "chan": {"required": True, "type": "str"} }}, - "override-analysis": {"required": False, "type": "str", + "override_analysis": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-band": {"required": False, "type": "str", + "override_band": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-channel": {"required": False, "type": "str", + "override_channel": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-txpower": {"required": False, "type": "str", + "override_txpower": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-vaps": {"required": False, "type": "str", + "override_vaps": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "power-level": {"required": False, "type": "int"}, - "radio-id": {"required": False, "type": "int"}, - "spectrum-analysis": {"required": False, "type": "str", + "power_level": {"required": False, "type": "int"}, + "radio_id": {"required": False, "type": "int"}, + "spectrum_analysis": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "vap-all": {"required": False, "type": "str", + "vap_all": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "vaps": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }} }}, - "radio-2": {"required": False, "type": "dict", + "radio_2": {"required": False, "type": "dict", "options": { - "auto-power-high": {"required": False, "type": "int"}, - "auto-power-level": {"required": False, "type": "str", + "auto_power_high": {"required": False, "type": "int"}, + "auto_power_level": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "auto-power-low": {"required": False, "type": "int"}, + "auto_power_low": {"required": False, "type": "int"}, "band": {"required": False, "type": "str", "choices": ["802.11a", "802.11b", "802.11g", "802.11n", "802.11n-5G", "802.11n,g-only", @@ -944,44 +1056,44 @@ def main(): "options": { "chan": {"required": True, "type": "str"} }}, - "override-analysis": {"required": False, "type": "str", + "override_analysis": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-band": {"required": False, "type": "str", + "override_band": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-channel": {"required": False, "type": "str", + "override_channel": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-txpower": {"required": False, "type": "str", + "override_txpower": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-vaps": {"required": False, "type": "str", + "override_vaps": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "power-level": {"required": False, "type": "int"}, - "radio-id": {"required": False, "type": "int"}, - "spectrum-analysis": {"required": False, "type": "str", + "power_level": {"required": False, "type": "int"}, + "radio_id": {"required": False, "type": "int"}, + "spectrum_analysis": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "vap-all": {"required": False, "type": "str", + "vap_all": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "vaps": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }} }}, - "split-tunneling-acl": {"required": False, "type": "list", + "split_tunneling_acl": {"required": False, "type": "list", "options": { - "dest-ip": {"required": False, "type": "str"}, + "dest_ip": {"required": False, "type": "str"}, "id": {"required": True, "type": "int"} }}, - "split-tunneling-acl-local-ap-subnet": {"required": False, "type": "str", + "split_tunneling_acl_local_ap_subnet": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "split-tunneling-acl-path": {"required": False, "type": "str", + "split_tunneling_acl_path": {"required": False, "type": "str", "choices": ["tunnel", "local"]}, - "tun-mtu-downlink": {"required": False, "type": "int"}, - "tun-mtu-uplink": {"required": False, "type": "int"}, - "wan-port-mode": {"required": False, "type": "str", + "tun_mtu_downlink": {"required": False, "type": "int"}, + "tun_mtu_uplink": {"required": False, "type": "int"}, + "wan_port_mode": {"required": False, "type": "str", "choices": ["wan-lan", "wan-only"]}, - "wtp-id": {"required": True, "type": "str"}, - "wtp-mode": {"required": False, "type": "str", + "wtp_id": {"required": False, "type": "str"}, + "wtp_mode": {"required": False, "type": "str", "choices": ["normal", "remote"]}, - "wtp-profile": {"required": False, "type": "str"} + "wtp_profile": {"required": False, "type": "str"} } } @@ -989,14 +1101,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") - 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_wireless_controller(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_wireless_controller(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_wireless_controller(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_wireless_controller_wtp_profile.py b/lib/ansible/modules/network/fortios/fortios_wireless_controller_wtp_profile.py index d84f0d38c42..d5a9ac1e178 100644 --- a/lib/ansible/modules/network/fortios/fortios_wireless_controller_wtp_profile.py +++ b/lib/ansible/modules/network/fortios/fortios_wireless_controller_wtp_profile.py @@ -26,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_wireless_controller_wtp_profile short_description: Configure WTP profiles or FortiAP profiles that define radio settings for manageable FortiAP platforms in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify wireless_controller feature and wtp_profile category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -41,51 +41,66 @@ 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: 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 wireless_controller_wtp_profile: description: - Configure WTP profiles or FortiAP profiles that define radio settings for manageable FortiAP platforms. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent allowaccess: description: - Control management access to the managed WTP, FortiAP, or AP. Separate entries with a space. + type: str choices: - telnet - http - https - ssh - ap-country: + ap_country: description: - - Country in which this WTP, FortiAP or AP will operate (default = US). + - Country in which this WTP, FortiAP or AP will operate . + type: str choices: - NA - AL @@ -215,15 +230,18 @@ options: - ZW - JP - CA - ble-profile: + ble_profile: description: - Bluetooth Low Energy profile name. Source wireless-controller.ble-profile.name. + type: str comment: description: - Comment. - control-message-offload: + type: str + control_message_offload: description: - Enable/disable CAPWAP control message data channel offload. + type: str choices: - ebp-frame - aeroscout-tag @@ -232,306 +250,367 @@ options: - sta-cap-list - stats - aeroscout-mu - deny-mac-list: + deny_mac_list: description: - List of MAC addresses that are denied access to this WTP, FortiAP, or AP. + type: list suboptions: id: description: - ID. required: true + type: int mac: description: - A WiFi device with this MAC address is denied access to this WTP, FortiAP or AP. - dtls-in-kernel: + type: str + dtls_in_kernel: description: - Enable/disable data channel DTLS in kernel. + type: str choices: - enable - disable - dtls-policy: + dtls_policy: description: - - WTP data channel DTLS policy (default = clear-text). + - WTP data channel DTLS policy . + type: str choices: - clear-text - dtls-enabled - ipsec-vpn - energy-efficient-ethernet: + energy_efficient_ethernet: description: - Enable/disable use of energy efficient Ethernet on WTP. + type: str choices: - enable - disable - ext-info-enable: + ext_info_enable: description: - Enable/disable station/VAP/radio extension information. + type: str choices: - enable - disable - handoff-roaming: + handoff_roaming: description: - - Enable/disable client load balancing during roaming to avoid roaming delay (default = disable). + - Enable/disable client load balancing during roaming to avoid roaming delay . + type: str choices: - enable - disable - handoff-rssi: + handoff_rssi: description: - - Minimum received signal strength indicator (RSSI) value for handoff (20 - 30, default = 25). - handoff-sta-thresh: + - Minimum received signal strength indicator (RSSI) value for handoff (20 - 30). + type: int + handoff_sta_thresh: description: - - Threshold value for AP handoff (5 - 35, default = 30). - ip-fragment-preventing: + - Threshold value for AP handoff. + type: int + ip_fragment_preventing: description: - - Select how to prevent IP fragmentation for CAPWAP tunneled control and data packets (default = tcp-mss-adjust). + - Select how to prevent IP fragmentation for CAPWAP tunneled control and data packets . + type: str choices: - tcp-mss-adjust - icmp-unreachable lan: description: - WTP LAN port mapping. + type: dict suboptions: - port-mode: + port_mode: description: - LAN port mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port-ssid: + port_ssid: description: - Bridge LAN port to SSID. Source wireless-controller.vap.name. - port1-mode: + type: str + port1_mode: description: - LAN port 1 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port1-ssid: + port1_ssid: description: - Bridge LAN port 1 to SSID. Source wireless-controller.vap.name. - port2-mode: + type: str + port2_mode: description: - LAN port 2 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port2-ssid: + port2_ssid: description: - Bridge LAN port 2 to SSID. Source wireless-controller.vap.name. - port3-mode: + type: str + port3_mode: description: - LAN port 3 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port3-ssid: + port3_ssid: description: - Bridge LAN port 3 to SSID. Source wireless-controller.vap.name. - port4-mode: + type: str + port4_mode: description: - LAN port 4 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port4-ssid: + port4_ssid: description: - Bridge LAN port 4 to SSID. Source wireless-controller.vap.name. - port5-mode: + type: str + port5_mode: description: - LAN port 5 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port5-ssid: + port5_ssid: description: - Bridge LAN port 5 to SSID. Source wireless-controller.vap.name. - port6-mode: + type: str + port6_mode: description: - LAN port 6 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port6-ssid: + port6_ssid: description: - Bridge LAN port 6 to SSID. Source wireless-controller.vap.name. - port7-mode: + type: str + port7_mode: description: - LAN port 7 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port7-ssid: + port7_ssid: description: - Bridge LAN port 7 to SSID. Source wireless-controller.vap.name. - port8-mode: + type: str + port8_mode: description: - LAN port 8 mode. + type: str choices: - offline - nat-to-wan - bridge-to-wan - bridge-to-ssid - port8-ssid: + port8_ssid: description: - Bridge LAN port 8 to SSID. Source wireless-controller.vap.name. + type: str lbs: description: - Set various location based service (LBS) options. + type: dict suboptions: aeroscout: description: - - Enable/disable AeroScout Real Time Location Service (RTLS) support. + - Enable/disable AeroScout Real Time Location Service (RTLS) support . + type: str choices: - enable - disable - aeroscout-ap-mac: + aeroscout_ap_mac: description: - - Use BSSID or board MAC address as AP MAC address in the Aeroscout AP message. + - Use BSSID or board MAC address as AP MAC address in AeroScout AP messages . + type: str choices: - bssid - board-mac - aeroscout-mmu-report: + aeroscout_mmu_report: description: - - Enable/disable MU compounded report. + - Enable/disable compounded AeroScout tag and MU report . + type: str choices: - enable - disable - aeroscout-mu: + aeroscout_mu: description: - - Enable/disable AeroScout support. + - Enable/disable AeroScout Mobile Unit (MU) support . + type: str choices: - enable - disable - aeroscout-mu-factor: + aeroscout_mu_factor: description: - - AeroScout Mobile Unit (MU) mode dilution factor (default = 20). - aeroscout-mu-timeout: + - AeroScout MU mode dilution factor . + type: int + aeroscout_mu_timeout: description: - - AeroScout MU mode timeout (0 - 65535 sec, default = 5). - aeroscout-server-ip: + - AeroScout MU mode timeout (0 - 65535 sec). + type: int + aeroscout_server_ip: description: - IP address of AeroScout server. - aeroscout-server-port: + type: str + aeroscout_server_port: description: - AeroScout server UDP listening port. - ekahau-blink-mode: + type: int + ekahau_blink_mode: description: - - Enable/disable Ekahua blink mode (also called AiRISTA Flow Blink Mode) to find the location of devices connected to a wireless - LAN (default = disable). + - Enable/disable Ekahau blink mode (now known as AiRISTA Flow) to track and locate WiFi tags . + type: str choices: - enable - disable - ekahau-tag: + ekahau_tag: description: - WiFi frame MAC address or WiFi Tag. - erc-server-ip: + type: str + erc_server_ip: description: - - IP address of Ekahua RTLS Controller (ERC). - erc-server-port: + - IP address of Ekahau RTLS Controller (ERC). + type: str + erc_server_port: description: - - Ekahua RTLS Controller (ERC) UDP listening port. + - Ekahau RTLS Controller (ERC) UDP listening port. + type: int fortipresence: description: - Enable/disable FortiPresence to monitor the location and activity of WiFi clients even if they don't connect to this WiFi - network (default = disable). + network . + type: str choices: - foreign - both - disable - fortipresence-frequency: + fortipresence_frequency: description: - - FortiPresence report transmit frequency (5 - 65535 sec, default = 30). - fortipresence-port: + - FortiPresence report transmit frequency (5 - 65535 sec). + type: int + fortipresence_port: description: - - FortiPresence server UDP listening port (default = 3000). - fortipresence-project: + - FortiPresence server UDP listening port . + type: int + fortipresence_project: description: - - FortiPresence project name (max. 16 characters, default = fortipresence). - fortipresence-rogue: + - FortiPresence project name (max. 16 characters). + type: str + fortipresence_rogue: description: - Enable/disable FortiPresence finding and reporting rogue APs. + type: str choices: - enable - disable - fortipresence-secret: + fortipresence_secret: description: - FortiPresence secret password (max. 16 characters). - fortipresence-server: + type: str + fortipresence_server: description: - FortiPresence server IP address. - fortipresence-unassoc: + type: str + fortipresence_unassoc: description: - Enable/disable FortiPresence finding and reporting unassociated stations. + type: str choices: - enable - disable - station-locate: + station_locate: description: - - Enable/disable client station locating services for all clients, whether associated or not (default = disable). + - Enable/disable client station locating services for all clients, whether associated or not . + type: str choices: - enable - disable - led-schedules: + led_schedules: description: - Recurring firewall schedules for illuminating LEDs on the FortiAP. If led-state is enabled, LEDs will be visible when at least one of the schedules is valid. Separate multiple schedule names with a space. + type: list suboptions: name: description: - LED schedule name. Source firewall.schedule.group.name firewall.schedule.recurring.name. required: true - led-state: + type: str + led_state: description: - - Enable/disable use of LEDs on WTP (default = disable). + - Enable/disable use of LEDs on WTP . + type: str choices: - enable - disable lldp: description: - - Enable/disable Link Layer Discovery Protocol (LLDP) for the WTP, FortiAP, or AP (default = disable). + - Enable/disable Link Layer Discovery Protocol (LLDP) for the WTP, FortiAP, or AP . + type: str choices: - enable - disable - login-passwd: + login_passwd: description: - Set the managed WTP, FortiAP, or AP's administrator password. - login-passwd-change: + type: str + login_passwd_change: description: - - Change or reset the administrator password of a managed WTP, FortiAP or AP (yes, default, or no, default = no). + - Change or reset the administrator password of a managed WTP, FortiAP or AP (yes, default, or no). + type: str choices: - yes - default - no - max-clients: + max_clients: description: - - Maximum number of stations (STAs) supported by the WTP (default = 0, meaning no client limitation). + - Maximum number of stations (STAs) supported by the WTP . + type: int name: description: - WTP (or FortiAP or AP) profile name. required: true + type: str platform: description: - WTP, FortiAP, or AP platform. + type: dict suboptions: type: description: - WTP, FortiAP or AP platform type. There are built-in WTP profiles for all supported FortiAP models. You can select a built-in profile and customize it or create a new profile. + type: str choices: - AP-11N - 220B @@ -585,84 +664,100 @@ options: - U24JEV - U321EV - U323EV - poe-mode: + poe_mode: description: - Set the WTP, FortiAP, or AP's PoE mode. + type: str choices: - auto - 8023af - 8023at - power-adapter - radio-1: + radio_1: description: - Configuration options for radio 1. + type: dict suboptions: amsdu: description: - - Enable/disable 802.11n AMSDU support. AMSDU can improve performance if supported by your WiFi clients (default = enable). + - Enable/disable 802.11n AMSDU support. AMSDU can improve performance if supported by your WiFi clients . + type: str choices: - enable - disable - ap-handoff: + ap_handoff: description: - - Enable/disable AP handoff of clients to other APs (default = disable). + - Enable/disable AP handoff of clients to other APs . + type: str choices: - enable - disable - ap-sniffer-addr: + ap_sniffer_addr: description: - MAC address to monitor. - ap-sniffer-bufsize: + type: str + ap_sniffer_bufsize: description: - - Sniffer buffer size (1 - 32 MB, default = 16). - ap-sniffer-chan: + - Sniffer buffer size (1 - 32 MB). + type: int + ap_sniffer_chan: description: - - Channel on which to operate the sniffer (default = 6). - ap-sniffer-ctl: + - Channel on which to operate the sniffer . + type: int + ap_sniffer_ctl: description: - - Enable/disable sniffer on WiFi control frame (default = enable). + - Enable/disable sniffer on WiFi control frame . + type: str choices: - enable - disable - ap-sniffer-data: + ap_sniffer_data: description: - - Enable/disable sniffer on WiFi data frame (default = enable). + - Enable/disable sniffer on WiFi data frame . + type: str choices: - enable - disable - ap-sniffer-mgmt-beacon: + ap_sniffer_mgmt_beacon: description: - - Enable/disable sniffer on WiFi management Beacon frames (default = enable). + - Enable/disable sniffer on WiFi management Beacon frames . + type: str choices: - enable - disable - ap-sniffer-mgmt-other: + ap_sniffer_mgmt_other: description: - - Enable/disable sniffer on WiFi management other frames (default = enable). + - Enable/disable sniffer on WiFi management other frames . + type: str choices: - enable - disable - ap-sniffer-mgmt-probe: + ap_sniffer_mgmt_probe: description: - - Enable/disable sniffer on WiFi management probe frames (default = enable). + - Enable/disable sniffer on WiFi management probe frames . + type: str choices: - enable - disable - auto-power-high: + auto_power_high: description: - Automatic transmit power high limit in dBm (the actual range of transmit power depends on the AP platform type). - auto-power-level: + type: int + auto_power_level: description: - - Enable/disable automatic power-level adjustment to prevent co-channel interference (default = disable). + - Enable/disable automatic power-level adjustment to prevent co-channel interference . + type: str choices: - enable - disable - auto-power-low: + auto_power_low: description: - Automatic transmission power low limit in dBm (the actual range of transmit power depends on the AP platform type). + type: int band: description: - WiFi band that Radio 1 operates on. + type: str choices: - 802.11a - 802.11b @@ -676,226 +771,268 @@ options: - 802.11n-5G-only - 802.11ac,n-only - 802.11ac-only - bandwidth-admission-control: + bandwidth_admission_control: description: - Enable/disable WiFi multimedia (WMM) bandwidth admission control to optimize WiFi bandwidth use. A request to join the wireless network is only allowed if the access point has enough bandwidth to support it. + type: str choices: - enable - disable - bandwidth-capacity: + bandwidth_capacity: description: - - Maximum bandwidth capacity allowed (1 - 600000 Kbps, default = 2000). - beacon-interval: + - Maximum bandwidth capacity allowed (1 - 600000 Kbps). + type: int + beacon_interval: description: - - Beacon interval. The time between beacon frames in msec (the actual range of beacon interval depends on the AP platform type, - default = 100). - call-admission-control: + - Beacon interval. The time between beacon frames in msec (the actual range of beacon interval depends on the AP platform type). + type: int + call_admission_control: description: - Enable/disable WiFi multimedia (WMM) call admission control to optimize WiFi bandwidth use for VoIP calls. New VoIP calls are only accepted if there is enough bandwidth available to support them. + type: str choices: - enable - disable - call-capacity: + call_capacity: description: - - Maximum number of Voice over WLAN (VoWLAN) phones supported by the radio (0 - 60, default = 10). + - Maximum number of Voice over WLAN (VoWLAN) phones supported by the radio (0 - 60). + type: int channel: description: - Selected list of wireless radio channels. + type: list suboptions: chan: description: - Channel number. required: true - channel-bonding: + type: str + channel_bonding: description: - "Channel bandwidth: 80, 40, or 20MHz. Channels may use both 20 and 40 by enabling coexistence." + type: str choices: - 80MHz - 40MHz - 20MHz - channel-utilization: + channel_utilization: description: - Enable/disable measuring channel utilization. + type: str choices: - enable - disable coexistence: description: - - Enable/disable allowing both HT20 and HT40 on the same radio (default = enable). + - Enable/disable allowing both HT20 and HT40 on the same radio . + type: str choices: - enable - disable darrp: description: - Enable/disable Distributed Automatic Radio Resource Provisioning (DARRP) to make sure the radio is always using the most optimal - channel (default = disable). + channel . + type: str choices: - enable - disable dtim: description: - - DTIM interval. The frequency to transmit Delivery Traffic Indication Message (or Map) (DTIM) messages (1 - 255, default = 1). - Set higher to save client battery life. - frag-threshold: + - DTIM interval. The frequency to transmit Delivery Traffic Indication Message (or Map) (DTIM) messages (1 - 255). Set higher to + save client battery life. + type: int + frag_threshold: description: - - Maximum packet size that can be sent without fragmentation (800 - 2346 bytes, default = 2346). - frequency-handoff: + - Maximum packet size that can be sent without fragmentation (800 - 2346 bytes). + type: int + frequency_handoff: description: - - Enable/disable frequency handoff of clients to other channels (default = disable). + - Enable/disable frequency handoff of clients to other channels . + type: str choices: - enable - disable - max-clients: + max_clients: description: - Maximum number of stations (STAs) or WiFi clients supported by the radio. Range depends on the hardware. - max-distance: + type: int + max_distance: description: - - Maximum expected distance between the AP and clients (0 - 54000 m, default = 0). + - Maximum expected distance between the AP and clients (0 - 54000 m). + type: int mode: description: - Mode of radio 1. Radio 1 can be disabled, configured as an access point, a rogue AP monitor, or a sniffer. + type: str choices: - disabled - ap - monitor - sniffer - power-level: + power_level: description: - - Radio power level as a percentage of the maximum transmit power (0 - 100, default = 100). - powersave-optimize: + - Radio power level as a percentage of the maximum transmit power (0 - 100). + type: int + powersave_optimize: description: - Enable client power-saving features such as TIM, AC VO, and OBSS etc. + type: str choices: - tim - ac-vo - no-obss-scan - no-11b-rate - client-rate-follow - protection-mode: + protection_mode: description: - Enable/disable 802.11g protection modes to support backwards compatibility with older clients (rtscts, ctsonly, disable). + type: str choices: - rtscts - ctsonly - disable - radio-id: + radio_id: description: - radio-id - rts-threshold: + type: int + rts_threshold: description: - - Maximum packet size for RTS transmissions, specifying the maximum size of a data packet before RTS/CTS (256 - 2346 bytes, - default = 2346). - short-guard-interval: + - Maximum packet size for RTS transmissions, specifying the maximum size of a data packet before RTS/CTS (256 - 2346 bytes). + type: int + short_guard_interval: description: - Use either the short guard interval (Short GI) of 400 ns or the long guard interval (Long GI) of 800 ns. + type: str choices: - enable - disable - spectrum-analysis: + spectrum_analysis: description: - Enable/disable spectrum analysis to find interference that would negatively impact wireless performance. + type: str choices: - enable - disable - transmit-optimize: + transmit_optimize: description: - Packet transmission optimization options including power saving, aggregation limiting, retry limiting, etc. All are enabled by default. + type: str choices: - disable - power-save - aggr-limit - retry-limit - send-bar - vap-all: + vap_all: description: - - Enable/disable the automatic inheritance of all Virtual Access Points (VAPs) (default = enable). + - Enable/disable the automatic inheritance of all Virtual Access Points (VAPs) . + type: str choices: - enable - disable vaps: description: - Manually selected list of Virtual Access Points (VAPs). + type: list suboptions: name: description: - Virtual Access Point (VAP) name. Source wireless-controller.vap-group.name wireless-controller.vap.name. required: true - wids-profile: + type: str + wids_profile: description: - Wireless Intrusion Detection System (WIDS) profile name to assign to the radio. Source wireless-controller.wids-profile.name. - radio-2: + type: str + radio_2: description: - Configuration options for radio 2. + type: dict suboptions: amsdu: description: - - Enable/disable 802.11n AMSDU support. AMSDU can improve performance if supported by your WiFi clients (default = enable). + - Enable/disable 802.11n AMSDU support. AMSDU can improve performance if supported by your WiFi clients . + type: str choices: - enable - disable - ap-handoff: + ap_handoff: description: - - Enable/disable AP handoff of clients to other APs (default = disable). + - Enable/disable AP handoff of clients to other APs . + type: str choices: - enable - disable - ap-sniffer-addr: + ap_sniffer_addr: description: - MAC address to monitor. - ap-sniffer-bufsize: + type: str + ap_sniffer_bufsize: description: - - Sniffer buffer size (1 - 32 MB, default = 16). - ap-sniffer-chan: + - Sniffer buffer size (1 - 32 MB). + type: int + ap_sniffer_chan: description: - - Channel on which to operate the sniffer (default = 6). - ap-sniffer-ctl: + - Channel on which to operate the sniffer . + type: int + ap_sniffer_ctl: description: - - Enable/disable sniffer on WiFi control frame (default = enable). + - Enable/disable sniffer on WiFi control frame . + type: str choices: - enable - disable - ap-sniffer-data: + ap_sniffer_data: description: - - Enable/disable sniffer on WiFi data frame (default = enable). + - Enable/disable sniffer on WiFi data frame . + type: str choices: - enable - disable - ap-sniffer-mgmt-beacon: + ap_sniffer_mgmt_beacon: description: - - Enable/disable sniffer on WiFi management Beacon frames (default = enable). + - Enable/disable sniffer on WiFi management Beacon frames . + type: str choices: - enable - disable - ap-sniffer-mgmt-other: + ap_sniffer_mgmt_other: description: - - Enable/disable sniffer on WiFi management other frames (default = enable). + - Enable/disable sniffer on WiFi management other frames . + type: str choices: - enable - disable - ap-sniffer-mgmt-probe: + ap_sniffer_mgmt_probe: description: - - Enable/disable sniffer on WiFi management probe frames (default = enable). + - Enable/disable sniffer on WiFi management probe frames . + type: str choices: - enable - disable - auto-power-high: + auto_power_high: description: - Automatic transmit power high limit in dBm (the actual range of transmit power depends on the AP platform type). - auto-power-level: + type: int + auto_power_level: description: - - Enable/disable automatic power-level adjustment to prevent co-channel interference (default = disable). + - Enable/disable automatic power-level adjustment to prevent co-channel interference . + type: str choices: - enable - disable - auto-power-low: + auto_power_low: description: - Automatic transmission power low limit in dBm (the actual range of transmit power depends on the AP platform type). + type: int band: description: - WiFi band that Radio 2 operates on. + type: str choices: - 802.11a - 802.11b @@ -909,188 +1046,223 @@ options: - 802.11n-5G-only - 802.11ac,n-only - 802.11ac-only - bandwidth-admission-control: + bandwidth_admission_control: description: - Enable/disable WiFi multimedia (WMM) bandwidth admission control to optimize WiFi bandwidth use. A request to join the wireless network is only allowed if the access point has enough bandwidth to support it. + type: str choices: - enable - disable - bandwidth-capacity: + bandwidth_capacity: description: - - Maximum bandwidth capacity allowed (1 - 600000 Kbps, default = 2000). - beacon-interval: + - Maximum bandwidth capacity allowed (1 - 600000 Kbps). + type: int + beacon_interval: description: - - Beacon interval. The time between beacon frames in msec (the actual range of beacon interval depends on the AP platform type, - default = 100). - call-admission-control: + - Beacon interval. The time between beacon frames in msec (the actual range of beacon interval depends on the AP platform type). + type: int + call_admission_control: description: - Enable/disable WiFi multimedia (WMM) call admission control to optimize WiFi bandwidth use for VoIP calls. New VoIP calls are only accepted if there is enough bandwidth available to support them. + type: str choices: - enable - disable - call-capacity: + call_capacity: description: - - Maximum number of Voice over WLAN (VoWLAN) phones supported by the radio (0 - 60, default = 10). + - Maximum number of Voice over WLAN (VoWLAN) phones supported by the radio (0 - 60). + type: int channel: description: - Selected list of wireless radio channels. + type: list suboptions: chan: description: - Channel number. required: true - channel-bonding: + type: str + channel_bonding: description: - "Channel bandwidth: 80, 40, or 20MHz. Channels may use both 20 and 40 by enabling coexistence." + type: str choices: - 80MHz - 40MHz - 20MHz - channel-utilization: + channel_utilization: description: - Enable/disable measuring channel utilization. + type: str choices: - enable - disable coexistence: description: - - Enable/disable allowing both HT20 and HT40 on the same radio (default = enable). + - Enable/disable allowing both HT20 and HT40 on the same radio . + type: str choices: - enable - disable darrp: description: - Enable/disable Distributed Automatic Radio Resource Provisioning (DARRP) to make sure the radio is always using the most optimal - channel (default = disable). + channel . + type: str choices: - enable - disable dtim: description: - - DTIM interval. The frequency to transmit Delivery Traffic Indication Message (or Map) (DTIM) messages (1 - 255, default = 1). - Set higher to save client battery life. - frag-threshold: + - DTIM interval. The frequency to transmit Delivery Traffic Indication Message (or Map) (DTIM) messages (1 - 255). Set higher to + save client battery life. + type: int + frag_threshold: description: - - Maximum packet size that can be sent without fragmentation (800 - 2346 bytes, default = 2346). - frequency-handoff: + - Maximum packet size that can be sent without fragmentation (800 - 2346 bytes). + type: int + frequency_handoff: description: - - Enable/disable frequency handoff of clients to other channels (default = disable). + - Enable/disable frequency handoff of clients to other channels . + type: str choices: - enable - disable - max-clients: + max_clients: description: - Maximum number of stations (STAs) or WiFi clients supported by the radio. Range depends on the hardware. - max-distance: + type: int + max_distance: description: - - Maximum expected distance between the AP and clients (0 - 54000 m, default = 0). + - Maximum expected distance between the AP and clients (0 - 54000 m). + type: int mode: description: - Mode of radio 2. Radio 2 can be disabled, configured as an access point, a rogue AP monitor, or a sniffer. + type: str choices: - disabled - ap - monitor - sniffer - power-level: + power_level: description: - - Radio power level as a percentage of the maximum transmit power (0 - 100, default = 100). - powersave-optimize: + - Radio power level as a percentage of the maximum transmit power (0 - 100). + type: int + powersave_optimize: description: - Enable client power-saving features such as TIM, AC VO, and OBSS etc. + type: str choices: - tim - ac-vo - no-obss-scan - no-11b-rate - client-rate-follow - protection-mode: + protection_mode: description: - Enable/disable 802.11g protection modes to support backwards compatibility with older clients (rtscts, ctsonly, disable). + type: str choices: - rtscts - ctsonly - disable - radio-id: + radio_id: description: - radio-id - rts-threshold: + type: int + rts_threshold: description: - - Maximum packet size for RTS transmissions, specifying the maximum size of a data packet before RTS/CTS (256 - 2346 bytes, - default = 2346). - short-guard-interval: + - Maximum packet size for RTS transmissions, specifying the maximum size of a data packet before RTS/CTS (256 - 2346 bytes). + type: int + short_guard_interval: description: - Use either the short guard interval (Short GI) of 400 ns or the long guard interval (Long GI) of 800 ns. + type: str choices: - enable - disable - spectrum-analysis: + spectrum_analysis: description: - Enable/disable spectrum analysis to find interference that would negatively impact wireless performance. + type: str choices: - enable - disable - transmit-optimize: + transmit_optimize: description: - Packet transmission optimization options including power saving, aggregation limiting, retry limiting, etc. All are enabled by default. + type: str choices: - disable - power-save - aggr-limit - retry-limit - send-bar - vap-all: + vap_all: description: - - Enable/disable the automatic inheritance of all Virtual Access Points (VAPs) (default = enable). + - Enable/disable the automatic inheritance of all Virtual Access Points (VAPs) . + type: str choices: - enable - disable vaps: description: - Manually selected list of Virtual Access Points (VAPs). + type: list suboptions: name: description: - Virtual Access Point (VAP) name. Source wireless-controller.vap-group.name wireless-controller.vap.name. required: true - wids-profile: + type: str + wids_profile: description: - Wireless Intrusion Detection System (WIDS) profile name to assign to the radio. Source wireless-controller.wids-profile.name. - split-tunneling-acl: + type: str + split_tunneling_acl: description: - Split tunneling ACL filter list. + type: list suboptions: - dest-ip: + dest_ip: description: - Destination IP and mask for the split-tunneling subnet. + type: str id: description: - ID. required: true - split-tunneling-acl-local-ap-subnet: + type: int + split_tunneling_acl_local_ap_subnet: description: - - Enable/disable automatically adding local subnetwork of FortiAP to split-tunneling ACL (default = disable). + - Enable/disable automatically adding local subnetwork of FortiAP to split-tunneling ACL . + type: str choices: - enable - disable - split-tunneling-acl-path: + split_tunneling_acl_path: description: - Split tunneling ACL path is local/tunnel. + type: str choices: - tunnel - local - tun-mtu-downlink: + tun_mtu_downlink: description: - - Downlink CAPWAP tunnel MTU (0, 576, or 1500 bytes, default = 0). - tun-mtu-uplink: + - Downlink CAPWAP tunnel MTU (0, 576, or 1500 bytes). + type: int + tun_mtu_uplink: description: - - Uplink CAPWAP tunnel MTU (0, 576, or 1500 bytes, default = 0). - wan-port-mode: + - Uplink CAPWAP tunnel MTU (0, 576, or 1500 bytes). + type: int + wan_port_mode: description: - Enable/disable using a WAN port as a LAN port. + type: str choices: - wan-lan - wan-only @@ -1103,6 +1275,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure WTP profiles or FortiAP profiles that define radio settings for manageable FortiAP platforms. fortios_wireless_controller_wtp_profile: @@ -1111,179 +1284,179 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" wireless_controller_wtp_profile: - state: "present" allowaccess: "telnet" - ap-country: "NA" - ble-profile: " (source wireless-controller.ble-profile.name)" + ap_country: "NA" + ble_profile: " (source wireless-controller.ble-profile.name)" comment: "Comment." - control-message-offload: "ebp-frame" - deny-mac-list: + control_message_offload: "ebp-frame" + deny_mac_list: - id: "9" mac: "" - dtls-in-kernel: "enable" - dtls-policy: "clear-text" - energy-efficient-ethernet: "enable" - ext-info-enable: "enable" - handoff-roaming: "enable" - handoff-rssi: "16" - handoff-sta-thresh: "17" - ip-fragment-preventing: "tcp-mss-adjust" + dtls_in_kernel: "enable" + dtls_policy: "clear-text" + energy_efficient_ethernet: "enable" + ext_info_enable: "enable" + handoff_roaming: "enable" + handoff_rssi: "16" + handoff_sta_thresh: "17" + ip_fragment_preventing: "tcp-mss-adjust" lan: - port-mode: "offline" - port-ssid: " (source wireless-controller.vap.name)" - port1-mode: "offline" - port1-ssid: " (source wireless-controller.vap.name)" - port2-mode: "offline" - port2-ssid: " (source wireless-controller.vap.name)" - port3-mode: "offline" - port3-ssid: " (source wireless-controller.vap.name)" - port4-mode: "offline" - port4-ssid: " (source wireless-controller.vap.name)" - port5-mode: "offline" - port5-ssid: " (source wireless-controller.vap.name)" - port6-mode: "offline" - port6-ssid: " (source wireless-controller.vap.name)" - port7-mode: "offline" - port7-ssid: " (source wireless-controller.vap.name)" - port8-mode: "offline" - port8-ssid: " (source wireless-controller.vap.name)" + port_mode: "offline" + port_ssid: " (source wireless-controller.vap.name)" + port1_mode: "offline" + port1_ssid: " (source wireless-controller.vap.name)" + port2_mode: "offline" + port2_ssid: " (source wireless-controller.vap.name)" + port3_mode: "offline" + port3_ssid: " (source wireless-controller.vap.name)" + port4_mode: "offline" + port4_ssid: " (source wireless-controller.vap.name)" + port5_mode: "offline" + port5_ssid: " (source wireless-controller.vap.name)" + port6_mode: "offline" + port6_ssid: " (source wireless-controller.vap.name)" + port7_mode: "offline" + port7_ssid: " (source wireless-controller.vap.name)" + port8_mode: "offline" + port8_ssid: " (source wireless-controller.vap.name)" lbs: aeroscout: "enable" - aeroscout-ap-mac: "bssid" - aeroscout-mmu-report: "enable" - aeroscout-mu: "enable" - aeroscout-mu-factor: "43" - aeroscout-mu-timeout: "44" - aeroscout-server-ip: "" - aeroscout-server-port: "46" - ekahau-blink-mode: "enable" - ekahau-tag: "" - erc-server-ip: "" - erc-server-port: "50" + aeroscout_ap_mac: "bssid" + aeroscout_mmu_report: "enable" + aeroscout_mu: "enable" + aeroscout_mu_factor: "43" + aeroscout_mu_timeout: "44" + aeroscout_server_ip: "" + aeroscout_server_port: "46" + ekahau_blink_mode: "enable" + ekahau_tag: "" + erc_server_ip: "" + erc_server_port: "50" fortipresence: "foreign" - fortipresence-frequency: "52" - fortipresence-port: "53" - fortipresence-project: "" - fortipresence-rogue: "enable" - fortipresence-secret: "" - fortipresence-server: "" - fortipresence-unassoc: "enable" - station-locate: "enable" - led-schedules: + fortipresence_frequency: "52" + fortipresence_port: "53" + fortipresence_project: "" + fortipresence_rogue: "enable" + fortipresence_secret: "" + fortipresence_server: "" + fortipresence_unassoc: "enable" + station_locate: "enable" + led_schedules: - name: "default_name_61 (source firewall.schedule.group.name firewall.schedule.recurring.name)" - led-state: "enable" + led_state: "enable" lldp: "enable" - login-passwd: "" - login-passwd-change: "yes" - max-clients: "66" + login_passwd: "" + login_passwd_change: "yes" + max_clients: "66" name: "default_name_67" platform: type: "AP-11N" - poe-mode: "auto" - radio-1: + poe_mode: "auto" + radio_1: amsdu: "enable" - ap-handoff: "enable" - ap-sniffer-addr: "" - ap-sniffer-bufsize: "75" - ap-sniffer-chan: "76" - ap-sniffer-ctl: "enable" - ap-sniffer-data: "enable" - ap-sniffer-mgmt-beacon: "enable" - ap-sniffer-mgmt-other: "enable" - ap-sniffer-mgmt-probe: "enable" - auto-power-high: "82" - auto-power-level: "enable" - auto-power-low: "84" + ap_handoff: "enable" + ap_sniffer_addr: "" + ap_sniffer_bufsize: "75" + ap_sniffer_chan: "76" + ap_sniffer_ctl: "enable" + ap_sniffer_data: "enable" + ap_sniffer_mgmt_beacon: "enable" + ap_sniffer_mgmt_other: "enable" + ap_sniffer_mgmt_probe: "enable" + auto_power_high: "82" + auto_power_level: "enable" + auto_power_low: "84" band: "802.11a" - bandwidth-admission-control: "enable" - bandwidth-capacity: "87" - beacon-interval: "88" - call-admission-control: "enable" - call-capacity: "90" + bandwidth_admission_control: "enable" + bandwidth_capacity: "87" + beacon_interval: "88" + call_admission_control: "enable" + call_capacity: "90" channel: - chan: "" - channel-bonding: "80MHz" - channel-utilization: "enable" + channel_bonding: "80MHz" + channel_utilization: "enable" coexistence: "enable" darrp: "enable" dtim: "97" - frag-threshold: "98" - frequency-handoff: "enable" - max-clients: "100" - max-distance: "101" + frag_threshold: "98" + frequency_handoff: "enable" + max_clients: "100" + max_distance: "101" mode: "disabled" - power-level: "103" - powersave-optimize: "tim" - protection-mode: "rtscts" - radio-id: "106" - rts-threshold: "107" - short-guard-interval: "enable" - spectrum-analysis: "enable" - transmit-optimize: "disable" - vap-all: "enable" + power_level: "103" + powersave_optimize: "tim" + protection_mode: "rtscts" + radio_id: "106" + rts_threshold: "107" + short_guard_interval: "enable" + spectrum_analysis: "enable" + transmit_optimize: "disable" + vap_all: "enable" vaps: - name: "default_name_113 (source wireless-controller.vap-group.name wireless-controller.vap.name)" - wids-profile: " (source wireless-controller.wids-profile.name)" - radio-2: + wids_profile: " (source wireless-controller.wids-profile.name)" + radio_2: amsdu: "enable" - ap-handoff: "enable" - ap-sniffer-addr: "" - ap-sniffer-bufsize: "119" - ap-sniffer-chan: "120" - ap-sniffer-ctl: "enable" - ap-sniffer-data: "enable" - ap-sniffer-mgmt-beacon: "enable" - ap-sniffer-mgmt-other: "enable" - ap-sniffer-mgmt-probe: "enable" - auto-power-high: "126" - auto-power-level: "enable" - auto-power-low: "128" + ap_handoff: "enable" + ap_sniffer_addr: "" + ap_sniffer_bufsize: "119" + ap_sniffer_chan: "120" + ap_sniffer_ctl: "enable" + ap_sniffer_data: "enable" + ap_sniffer_mgmt_beacon: "enable" + ap_sniffer_mgmt_other: "enable" + ap_sniffer_mgmt_probe: "enable" + auto_power_high: "126" + auto_power_level: "enable" + auto_power_low: "128" band: "802.11a" - bandwidth-admission-control: "enable" - bandwidth-capacity: "131" - beacon-interval: "132" - call-admission-control: "enable" - call-capacity: "134" + bandwidth_admission_control: "enable" + bandwidth_capacity: "131" + beacon_interval: "132" + call_admission_control: "enable" + call_capacity: "134" channel: - chan: "" - channel-bonding: "80MHz" - channel-utilization: "enable" + channel_bonding: "80MHz" + channel_utilization: "enable" coexistence: "enable" darrp: "enable" dtim: "141" - frag-threshold: "142" - frequency-handoff: "enable" - max-clients: "144" - max-distance: "145" + frag_threshold: "142" + frequency_handoff: "enable" + max_clients: "144" + max_distance: "145" mode: "disabled" - power-level: "147" - powersave-optimize: "tim" - protection-mode: "rtscts" - radio-id: "150" - rts-threshold: "151" - short-guard-interval: "enable" - spectrum-analysis: "enable" - transmit-optimize: "disable" - vap-all: "enable" + power_level: "147" + powersave_optimize: "tim" + protection_mode: "rtscts" + radio_id: "150" + rts_threshold: "151" + short_guard_interval: "enable" + spectrum_analysis: "enable" + transmit_optimize: "disable" + vap_all: "enable" vaps: - name: "default_name_157 (source wireless-controller.vap-group.name wireless-controller.vap.name)" - wids-profile: " (source wireless-controller.wids-profile.name)" - split-tunneling-acl: + wids_profile: " (source wireless-controller.wids-profile.name)" + split_tunneling_acl: - - dest-ip: "" + dest_ip: "" id: "161" - split-tunneling-acl-local-ap-subnet: "enable" - split-tunneling-acl-path: "tunnel" - tun-mtu-downlink: "164" - tun-mtu-uplink: "165" - wan-port-mode: "wan-lan" + split_tunneling_acl_local_ap_subnet: "enable" + split_tunneling_acl_path: "tunnel" + tun_mtu_downlink: "164" + tun_mtu_uplink: "165" + wan_port_mode: "wan-lan" ''' RETURN = ''' @@ -1346,12 +1519,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 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']: @@ -1359,21 +1536,21 @@ def login(data, fos): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_wireless_controller_wtp_profile_data(json): - option_list = ['allowaccess', 'ap-country', 'ble-profile', - 'comment', 'control-message-offload', 'deny-mac-list', - 'dtls-in-kernel', 'dtls-policy', 'energy-efficient-ethernet', - 'ext-info-enable', 'handoff-roaming', 'handoff-rssi', - 'handoff-sta-thresh', 'ip-fragment-preventing', 'lan', - 'lbs', 'led-schedules', 'led-state', - 'lldp', 'login-passwd', 'login-passwd-change', - 'max-clients', 'name', 'platform', - 'poe-mode', 'radio-1', 'radio-2', - 'split-tunneling-acl', 'split-tunneling-acl-local-ap-subnet', 'split-tunneling-acl-path', - 'tun-mtu-downlink', 'tun-mtu-uplink', 'wan-port-mode'] + option_list = ['allowaccess', 'ap_country', 'ble_profile', + 'comment', 'control_message_offload', 'deny_mac_list', + 'dtls_in_kernel', 'dtls_policy', 'energy_efficient_ethernet', + 'ext_info_enable', 'handoff_roaming', 'handoff_rssi', + 'handoff_sta_thresh', 'ip_fragment_preventing', 'lan', + 'lbs', 'led_schedules', 'led_state', + 'lldp', 'login_passwd', 'login_passwd_change', + 'max_clients', 'name', 'platform', + 'poe_mode', 'radio_1', 'radio_2', + 'split_tunneling_acl', 'split_tunneling_acl_local_ap_subnet', 'split_tunneling_acl_path', + 'tun_mtu_downlink', 'tun_mtu_uplink', 'wan_port_mode'] dictionary = {} for attribute in option_list: @@ -1383,50 +1560,70 @@ def filter_wireless_controller_wtp_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 wireless_controller_wtp_profile(data, fos): vdom = data['vdom'] + state = data['state'] wireless_controller_wtp_profile_data = data['wireless_controller_wtp_profile'] - filtered_data = filter_wireless_controller_wtp_profile_data(wireless_controller_wtp_profile_data) + filtered_data = underscore_to_hyphen(filter_wireless_controller_wtp_profile_data(wireless_controller_wtp_profile_data)) - if wireless_controller_wtp_profile_data['state'] == "present": + if state == "present": return fos.set('wireless-controller', 'wtp-profile', data=filtered_data, vdom=vdom) - elif wireless_controller_wtp_profile_data['state'] == "absent": + elif state == "absent": return fos.delete('wireless-controller', 'wtp-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_wireless_controller(data, fos): - login(data, fos) if data['wireless_controller_wtp_profile']: resp = wireless_controller_wtp_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"]}, "wireless_controller_wtp_profile": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "allowaccess": {"required": False, "type": "str", "choices": ["telnet", "http", "https", "ssh"]}, - "ap-country": {"required": False, "type": "str", + "ap_country": {"required": False, "type": "str", "choices": ["NA", "AL", "DZ", "AO", "AR", "AM", "AU", "AT", "AZ", @@ -1470,115 +1667,115 @@ def main(): "UZ", "VE", "VN", "YE", "ZB", "ZW", "JP", "CA"]}, - "ble-profile": {"required": False, "type": "str"}, + "ble_profile": {"required": False, "type": "str"}, "comment": {"required": False, "type": "str"}, - "control-message-offload": {"required": False, "type": "str", + "control_message_offload": {"required": False, "type": "str", "choices": ["ebp-frame", "aeroscout-tag", "ap-list", "sta-list", "sta-cap-list", "stats", "aeroscout-mu"]}, - "deny-mac-list": {"required": False, "type": "list", + "deny_mac_list": {"required": False, "type": "list", "options": { "id": {"required": True, "type": "int"}, "mac": {"required": False, "type": "str"} }}, - "dtls-in-kernel": {"required": False, "type": "str", + "dtls_in_kernel": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "dtls-policy": {"required": False, "type": "str", + "dtls_policy": {"required": False, "type": "str", "choices": ["clear-text", "dtls-enabled", "ipsec-vpn"]}, - "energy-efficient-ethernet": {"required": False, "type": "str", + "energy_efficient_ethernet": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ext-info-enable": {"required": False, "type": "str", + "ext_info_enable": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "handoff-roaming": {"required": False, "type": "str", + "handoff_roaming": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "handoff-rssi": {"required": False, "type": "int"}, - "handoff-sta-thresh": {"required": False, "type": "int"}, - "ip-fragment-preventing": {"required": False, "type": "str", + "handoff_rssi": {"required": False, "type": "int"}, + "handoff_sta_thresh": {"required": False, "type": "int"}, + "ip_fragment_preventing": {"required": False, "type": "str", "choices": ["tcp-mss-adjust", "icmp-unreachable"]}, "lan": {"required": False, "type": "dict", "options": { - "port-mode": {"required": False, "type": "str", + "port_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port-ssid": {"required": False, "type": "str"}, - "port1-mode": {"required": False, "type": "str", + "port_ssid": {"required": False, "type": "str"}, + "port1_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port1-ssid": {"required": False, "type": "str"}, - "port2-mode": {"required": False, "type": "str", + "port1_ssid": {"required": False, "type": "str"}, + "port2_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port2-ssid": {"required": False, "type": "str"}, - "port3-mode": {"required": False, "type": "str", + "port2_ssid": {"required": False, "type": "str"}, + "port3_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port3-ssid": {"required": False, "type": "str"}, - "port4-mode": {"required": False, "type": "str", + "port3_ssid": {"required": False, "type": "str"}, + "port4_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port4-ssid": {"required": False, "type": "str"}, - "port5-mode": {"required": False, "type": "str", + "port4_ssid": {"required": False, "type": "str"}, + "port5_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port5-ssid": {"required": False, "type": "str"}, - "port6-mode": {"required": False, "type": "str", + "port5_ssid": {"required": False, "type": "str"}, + "port6_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port6-ssid": {"required": False, "type": "str"}, - "port7-mode": {"required": False, "type": "str", + "port6_ssid": {"required": False, "type": "str"}, + "port7_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port7-ssid": {"required": False, "type": "str"}, - "port8-mode": {"required": False, "type": "str", + "port7_ssid": {"required": False, "type": "str"}, + "port8_mode": {"required": False, "type": "str", "choices": ["offline", "nat-to-wan", "bridge-to-wan", "bridge-to-ssid"]}, - "port8-ssid": {"required": False, "type": "str"} + "port8_ssid": {"required": False, "type": "str"} }}, "lbs": {"required": False, "type": "dict", "options": { "aeroscout": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "aeroscout-ap-mac": {"required": False, "type": "str", + "aeroscout_ap_mac": {"required": False, "type": "str", "choices": ["bssid", "board-mac"]}, - "aeroscout-mmu-report": {"required": False, "type": "str", + "aeroscout_mmu_report": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "aeroscout-mu": {"required": False, "type": "str", + "aeroscout_mu": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "aeroscout-mu-factor": {"required": False, "type": "int"}, - "aeroscout-mu-timeout": {"required": False, "type": "int"}, - "aeroscout-server-ip": {"required": False, "type": "str"}, - "aeroscout-server-port": {"required": False, "type": "int"}, - "ekahau-blink-mode": {"required": False, "type": "str", + "aeroscout_mu_factor": {"required": False, "type": "int"}, + "aeroscout_mu_timeout": {"required": False, "type": "int"}, + "aeroscout_server_ip": {"required": False, "type": "str"}, + "aeroscout_server_port": {"required": False, "type": "int"}, + "ekahau_blink_mode": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ekahau-tag": {"required": False, "type": "str"}, - "erc-server-ip": {"required": False, "type": "str"}, - "erc-server-port": {"required": False, "type": "int"}, + "ekahau_tag": {"required": False, "type": "str"}, + "erc_server_ip": {"required": False, "type": "str"}, + "erc_server_port": {"required": False, "type": "int"}, "fortipresence": {"required": False, "type": "str", "choices": ["foreign", "both", "disable"]}, - "fortipresence-frequency": {"required": False, "type": "int"}, - "fortipresence-port": {"required": False, "type": "int"}, - "fortipresence-project": {"required": False, "type": "str"}, - "fortipresence-rogue": {"required": False, "type": "str", + "fortipresence_frequency": {"required": False, "type": "int"}, + "fortipresence_port": {"required": False, "type": "int"}, + "fortipresence_project": {"required": False, "type": "str"}, + "fortipresence_rogue": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "fortipresence-secret": {"required": False, "type": "str"}, - "fortipresence-server": {"required": False, "type": "str"}, - "fortipresence-unassoc": {"required": False, "type": "str", + "fortipresence_secret": {"required": False, "type": "str"}, + "fortipresence_server": {"required": False, "type": "str"}, + "fortipresence_unassoc": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "station-locate": {"required": False, "type": "str", + "station_locate": {"required": False, "type": "str", "choices": ["enable", "disable"]} }}, - "led-schedules": {"required": False, "type": "list", + "led_schedules": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, - "led-state": {"required": False, "type": "str", + "led_state": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "lldp": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "login-passwd": {"required": False, "type": "str"}, - "login-passwd-change": {"required": False, "type": "str", + "login_passwd": {"required": False, "type": "str"}, + "login_passwd_change": {"required": False, "type": "str", "choices": ["yes", "default", "no"]}, - "max-clients": {"required": False, "type": "int"}, + "max_clients": {"required": False, "type": "int"}, "name": {"required": True, "type": "str"}, "platform": {"required": False, "type": "dict", "options": { @@ -1602,179 +1799,179 @@ def main(): "U223EV", "U24JEV", "U321EV", "U323EV"]} }}, - "poe-mode": {"required": False, "type": "str", + "poe_mode": {"required": False, "type": "str", "choices": ["auto", "8023af", "8023at", "power-adapter"]}, - "radio-1": {"required": False, "type": "dict", + "radio_1": {"required": False, "type": "dict", "options": { "amsdu": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ap-handoff": {"required": False, "type": "str", + "ap_handoff": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ap-sniffer-addr": {"required": False, "type": "str"}, - "ap-sniffer-bufsize": {"required": False, "type": "int"}, - "ap-sniffer-chan": {"required": False, "type": "int"}, - "ap-sniffer-ctl": {"required": False, "type": "str", + "ap_sniffer_addr": {"required": False, "type": "str"}, + "ap_sniffer_bufsize": {"required": False, "type": "int"}, + "ap_sniffer_chan": {"required": False, "type": "int"}, + "ap_sniffer_ctl": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ap-sniffer-data": {"required": False, "type": "str", + "ap_sniffer_data": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ap-sniffer-mgmt-beacon": {"required": False, "type": "str", + "ap_sniffer_mgmt_beacon": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ap-sniffer-mgmt-other": {"required": False, "type": "str", + "ap_sniffer_mgmt_other": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ap-sniffer-mgmt-probe": {"required": False, "type": "str", + "ap_sniffer_mgmt_probe": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "auto-power-high": {"required": False, "type": "int"}, - "auto-power-level": {"required": False, "type": "str", + "auto_power_high": {"required": False, "type": "int"}, + "auto_power_level": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "auto-power-low": {"required": False, "type": "int"}, + "auto_power_low": {"required": False, "type": "int"}, "band": {"required": False, "type": "str", "choices": ["802.11a", "802.11b", "802.11g", "802.11n", "802.11n-5G", "802.11ac", "802.11n,g-only", "802.11g-only", "802.11n-only", "802.11n-5G-only", "802.11ac,n-only", "802.11ac-only"]}, - "bandwidth-admission-control": {"required": False, "type": "str", + "bandwidth_admission_control": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "bandwidth-capacity": {"required": False, "type": "int"}, - "beacon-interval": {"required": False, "type": "int"}, - "call-admission-control": {"required": False, "type": "str", + "bandwidth_capacity": {"required": False, "type": "int"}, + "beacon_interval": {"required": False, "type": "int"}, + "call_admission_control": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "call-capacity": {"required": False, "type": "int"}, + "call_capacity": {"required": False, "type": "int"}, "channel": {"required": False, "type": "list", "options": { "chan": {"required": True, "type": "str"} }}, - "channel-bonding": {"required": False, "type": "str", + "channel_bonding": {"required": False, "type": "str", "choices": ["80MHz", "40MHz", "20MHz"]}, - "channel-utilization": {"required": False, "type": "str", + "channel_utilization": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "coexistence": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "darrp": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "dtim": {"required": False, "type": "int"}, - "frag-threshold": {"required": False, "type": "int"}, - "frequency-handoff": {"required": False, "type": "str", + "frag_threshold": {"required": False, "type": "int"}, + "frequency_handoff": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "max-clients": {"required": False, "type": "int"}, - "max-distance": {"required": False, "type": "int"}, + "max_clients": {"required": False, "type": "int"}, + "max_distance": {"required": False, "type": "int"}, "mode": {"required": False, "type": "str", "choices": ["disabled", "ap", "monitor", "sniffer"]}, - "power-level": {"required": False, "type": "int"}, - "powersave-optimize": {"required": False, "type": "str", + "power_level": {"required": False, "type": "int"}, + "powersave_optimize": {"required": False, "type": "str", "choices": ["tim", "ac-vo", "no-obss-scan", "no-11b-rate", "client-rate-follow"]}, - "protection-mode": {"required": False, "type": "str", + "protection_mode": {"required": False, "type": "str", "choices": ["rtscts", "ctsonly", "disable"]}, - "radio-id": {"required": False, "type": "int"}, - "rts-threshold": {"required": False, "type": "int"}, - "short-guard-interval": {"required": False, "type": "str", + "radio_id": {"required": False, "type": "int"}, + "rts_threshold": {"required": False, "type": "int"}, + "short_guard_interval": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "spectrum-analysis": {"required": False, "type": "str", + "spectrum_analysis": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "transmit-optimize": {"required": False, "type": "str", + "transmit_optimize": {"required": False, "type": "str", "choices": ["disable", "power-save", "aggr-limit", "retry-limit", "send-bar"]}, - "vap-all": {"required": False, "type": "str", + "vap_all": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "vaps": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, - "wids-profile": {"required": False, "type": "str"} + "wids_profile": {"required": False, "type": "str"} }}, - "radio-2": {"required": False, "type": "dict", + "radio_2": {"required": False, "type": "dict", "options": { "amsdu": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ap-handoff": {"required": False, "type": "str", + "ap_handoff": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ap-sniffer-addr": {"required": False, "type": "str"}, - "ap-sniffer-bufsize": {"required": False, "type": "int"}, - "ap-sniffer-chan": {"required": False, "type": "int"}, - "ap-sniffer-ctl": {"required": False, "type": "str", + "ap_sniffer_addr": {"required": False, "type": "str"}, + "ap_sniffer_bufsize": {"required": False, "type": "int"}, + "ap_sniffer_chan": {"required": False, "type": "int"}, + "ap_sniffer_ctl": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ap-sniffer-data": {"required": False, "type": "str", + "ap_sniffer_data": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ap-sniffer-mgmt-beacon": {"required": False, "type": "str", + "ap_sniffer_mgmt_beacon": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ap-sniffer-mgmt-other": {"required": False, "type": "str", + "ap_sniffer_mgmt_other": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ap-sniffer-mgmt-probe": {"required": False, "type": "str", + "ap_sniffer_mgmt_probe": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "auto-power-high": {"required": False, "type": "int"}, - "auto-power-level": {"required": False, "type": "str", + "auto_power_high": {"required": False, "type": "int"}, + "auto_power_level": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "auto-power-low": {"required": False, "type": "int"}, + "auto_power_low": {"required": False, "type": "int"}, "band": {"required": False, "type": "str", "choices": ["802.11a", "802.11b", "802.11g", "802.11n", "802.11n-5G", "802.11ac", "802.11n,g-only", "802.11g-only", "802.11n-only", "802.11n-5G-only", "802.11ac,n-only", "802.11ac-only"]}, - "bandwidth-admission-control": {"required": False, "type": "str", + "bandwidth_admission_control": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "bandwidth-capacity": {"required": False, "type": "int"}, - "beacon-interval": {"required": False, "type": "int"}, - "call-admission-control": {"required": False, "type": "str", + "bandwidth_capacity": {"required": False, "type": "int"}, + "beacon_interval": {"required": False, "type": "int"}, + "call_admission_control": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "call-capacity": {"required": False, "type": "int"}, + "call_capacity": {"required": False, "type": "int"}, "channel": {"required": False, "type": "list", "options": { "chan": {"required": True, "type": "str"} }}, - "channel-bonding": {"required": False, "type": "str", + "channel_bonding": {"required": False, "type": "str", "choices": ["80MHz", "40MHz", "20MHz"]}, - "channel-utilization": {"required": False, "type": "str", + "channel_utilization": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "coexistence": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "darrp": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "dtim": {"required": False, "type": "int"}, - "frag-threshold": {"required": False, "type": "int"}, - "frequency-handoff": {"required": False, "type": "str", + "frag_threshold": {"required": False, "type": "int"}, + "frequency_handoff": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "max-clients": {"required": False, "type": "int"}, - "max-distance": {"required": False, "type": "int"}, + "max_clients": {"required": False, "type": "int"}, + "max_distance": {"required": False, "type": "int"}, "mode": {"required": False, "type": "str", "choices": ["disabled", "ap", "monitor", "sniffer"]}, - "power-level": {"required": False, "type": "int"}, - "powersave-optimize": {"required": False, "type": "str", + "power_level": {"required": False, "type": "int"}, + "powersave_optimize": {"required": False, "type": "str", "choices": ["tim", "ac-vo", "no-obss-scan", "no-11b-rate", "client-rate-follow"]}, - "protection-mode": {"required": False, "type": "str", + "protection_mode": {"required": False, "type": "str", "choices": ["rtscts", "ctsonly", "disable"]}, - "radio-id": {"required": False, "type": "int"}, - "rts-threshold": {"required": False, "type": "int"}, - "short-guard-interval": {"required": False, "type": "str", + "radio_id": {"required": False, "type": "int"}, + "rts_threshold": {"required": False, "type": "int"}, + "short_guard_interval": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "spectrum-analysis": {"required": False, "type": "str", + "spectrum_analysis": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "transmit-optimize": {"required": False, "type": "str", + "transmit_optimize": {"required": False, "type": "str", "choices": ["disable", "power-save", "aggr-limit", "retry-limit", "send-bar"]}, - "vap-all": {"required": False, "type": "str", + "vap_all": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "vaps": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, - "wids-profile": {"required": False, "type": "str"} + "wids_profile": {"required": False, "type": "str"} }}, - "split-tunneling-acl": {"required": False, "type": "list", + "split_tunneling_acl": {"required": False, "type": "list", "options": { - "dest-ip": {"required": False, "type": "str"}, + "dest_ip": {"required": False, "type": "str"}, "id": {"required": True, "type": "int"} }}, - "split-tunneling-acl-local-ap-subnet": {"required": False, "type": "str", + "split_tunneling_acl_local_ap_subnet": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "split-tunneling-acl-path": {"required": False, "type": "str", + "split_tunneling_acl_path": {"required": False, "type": "str", "choices": ["tunnel", "local"]}, - "tun-mtu-downlink": {"required": False, "type": "int"}, - "tun-mtu-uplink": {"required": False, "type": "int"}, - "wan-port-mode": {"required": False, "type": "str", + "tun_mtu_downlink": {"required": False, "type": "int"}, + "tun_mtu_uplink": {"required": False, "type": "int"}, + "wan_port_mode": {"required": False, "type": "str", "choices": ["wan-lan", "wan-only"]} } @@ -1783,14 +1980,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") - 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_wireless_controller(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_wireless_controller(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_wireless_controller(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 652d7af4213..3aa414e6cfc 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -3712,51 +3712,13 @@ lib/ansible/modules/network/fortios/fortios_system_global.py validate-modules:E3 lib/ansible/modules/network/fortios/fortios_voip_profile.py validate-modules:E326 lib/ansible/modules/network/fortios/fortios_vpn_ipsec_manualkey.py validate-modules:E326 lib/ansible/modules/network/fortios/fortios_vpn_ipsec_manualkey_interface.py validate-modules:E326 -lib/ansible/modules/network/fortios/fortios_web_proxy_explicit.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_web_proxy_explicit.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_web_proxy_global.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_web_proxy_global.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_web_proxy_profile.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_web_proxy_profile.py validate-modules:E337 lib/ansible/modules/network/fortios/fortios_webfilter.py validate-modules:E326 lib/ansible/modules/network/fortios/fortios_webfilter.py validate-modules:E328 lib/ansible/modules/network/fortios/fortios_webfilter.py validate-modules:E336 lib/ansible/modules/network/fortios/fortios_webfilter.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_webfilter_fortiguard.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_webfilter_fortiguard.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_webfilter_ftgd_local_cat.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_webfilter_ftgd_local_rating.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_cache_setting.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_cache_setting.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_setting.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_setting.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_setting6.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_webfilter_ips_urlfilter_setting6.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_webfilter_override.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_webfilter_override.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_webfilter_profile.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_webfilter_profile.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_webfilter_search_engine.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_webfilter_search_engine.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_webfilter_urlfilter.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_webfilter_urlfilter.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_wireless_controller_global.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_wireless_controller_global.py validate-modules:E337 lib/ansible/modules/network/fortios/fortios_wireless_controller_setting.py validate-modules:E326 -lib/ansible/modules/network/fortios/fortios_wireless_controller_setting.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_wireless_controller_setting.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_wireless_controller_utm_profile.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_wireless_controller_utm_profile.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_wireless_controller_vap.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_wireless_controller_vap.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_wireless_controller_wids_profile.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_wireless_controller_wids_profile.py validate-modules:E337 lib/ansible/modules/network/fortios/fortios_wireless_controller_wtp.py validate-modules:E326 -lib/ansible/modules/network/fortios/fortios_wireless_controller_wtp.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_wireless_controller_wtp.py validate-modules:E337 lib/ansible/modules/network/fortios/fortios_wireless_controller_wtp_profile.py validate-modules:E326 -lib/ansible/modules/network/fortios/fortios_wireless_controller_wtp_profile.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_wireless_controller_wtp_profile.py validate-modules:E337 lib/ansible/modules/network/frr/frr_bgp.py validate-modules:E322 lib/ansible/modules/network/frr/frr_bgp.py validate-modules:E323 lib/ansible/modules/network/frr/frr_bgp.py validate-modules:E337 diff --git a/test/units/modules/network/fortios/test_fortios_web_proxy_explicit.py b/test/units/modules/network/fortios/test_fortios_web_proxy_explicit.py new file mode 100644 index 00000000000..013fa3a52cc --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_web_proxy_explicit.py @@ -0,0 +1,351 @@ +# 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_web_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_web_proxy_explicit.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_web_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', + 'web_proxy_explicit': { + 'ftp_incoming_port': 'test_value_3', + 'ftp_over_http': 'enable', + 'http_incoming_port': 'test_value_5', + 'https_incoming_port': 'test_value_6', + 'https_replacement_message': 'enable', + 'incoming_ip': 'test_value_8', + 'incoming_ip6': 'test_value_9', + 'ipv6_status': 'enable', + 'message_upon_server_error': 'enable', + 'outgoing_ip': 'test_value_12', + 'outgoing_ip6': 'test_value_13', + 'pac_file_data': 'test_value_14', + 'pac_file_name': 'test_value_15', + 'pac_file_server_port': 'test_value_16', + 'pac_file_server_status': 'enable', + 'pac_file_url': 'test_value_18', + 'pref_dns_result': 'ipv4', + 'realm': 'test_value_20', + 'sec_default_action': 'accept', + 'socks': 'enable', + 'socks_incoming_port': 'test_value_23', + 'ssl_algorithm': 'low', + 'status': 'enable', + 'strict_guest': 'enable', + 'trace_auth_no_rsp': 'enable', + 'unknown_http_version': 'reject' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_web_proxy_explicit.fortios_web_proxy(input_data, fos_instance) + + expected_data = { + 'ftp-incoming-port': 'test_value_3', + 'ftp-over-http': 'enable', + 'http-incoming-port': 'test_value_5', + 'https-incoming-port': 'test_value_6', + 'https-replacement-message': 'enable', + 'incoming-ip': 'test_value_8', + 'incoming-ip6': 'test_value_9', + 'ipv6-status': 'enable', + 'message-upon-server-error': 'enable', + 'outgoing-ip': 'test_value_12', + 'outgoing-ip6': 'test_value_13', + 'pac-file-data': 'test_value_14', + 'pac-file-name': 'test_value_15', + 'pac-file-server-port': 'test_value_16', + 'pac-file-server-status': 'enable', + 'pac-file-url': 'test_value_18', + 'pref-dns-result': 'ipv4', + 'realm': 'test_value_20', + 'sec-default-action': 'accept', + 'socks': 'enable', + 'socks-incoming-port': 'test_value_23', + 'ssl-algorithm': 'low', + 'status': 'enable', + 'strict-guest': 'enable', + 'trace-auth-no-rsp': 'enable', + 'unknown-http-version': 'reject' + } + + set_method_mock.assert_called_with('web-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_web_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', + 'web_proxy_explicit': { + 'ftp_incoming_port': 'test_value_3', + 'ftp_over_http': 'enable', + 'http_incoming_port': 'test_value_5', + 'https_incoming_port': 'test_value_6', + 'https_replacement_message': 'enable', + 'incoming_ip': 'test_value_8', + 'incoming_ip6': 'test_value_9', + 'ipv6_status': 'enable', + 'message_upon_server_error': 'enable', + 'outgoing_ip': 'test_value_12', + 'outgoing_ip6': 'test_value_13', + 'pac_file_data': 'test_value_14', + 'pac_file_name': 'test_value_15', + 'pac_file_server_port': 'test_value_16', + 'pac_file_server_status': 'enable', + 'pac_file_url': 'test_value_18', + 'pref_dns_result': 'ipv4', + 'realm': 'test_value_20', + 'sec_default_action': 'accept', + 'socks': 'enable', + 'socks_incoming_port': 'test_value_23', + 'ssl_algorithm': 'low', + 'status': 'enable', + 'strict_guest': 'enable', + 'trace_auth_no_rsp': 'enable', + 'unknown_http_version': 'reject' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_web_proxy_explicit.fortios_web_proxy(input_data, fos_instance) + + expected_data = { + 'ftp-incoming-port': 'test_value_3', + 'ftp-over-http': 'enable', + 'http-incoming-port': 'test_value_5', + 'https-incoming-port': 'test_value_6', + 'https-replacement-message': 'enable', + 'incoming-ip': 'test_value_8', + 'incoming-ip6': 'test_value_9', + 'ipv6-status': 'enable', + 'message-upon-server-error': 'enable', + 'outgoing-ip': 'test_value_12', + 'outgoing-ip6': 'test_value_13', + 'pac-file-data': 'test_value_14', + 'pac-file-name': 'test_value_15', + 'pac-file-server-port': 'test_value_16', + 'pac-file-server-status': 'enable', + 'pac-file-url': 'test_value_18', + 'pref-dns-result': 'ipv4', + 'realm': 'test_value_20', + 'sec-default-action': 'accept', + 'socks': 'enable', + 'socks-incoming-port': 'test_value_23', + 'ssl-algorithm': 'low', + 'status': 'enable', + 'strict-guest': 'enable', + 'trace-auth-no-rsp': 'enable', + 'unknown-http-version': 'reject' + } + + set_method_mock.assert_called_with('web-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_web_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', + 'web_proxy_explicit': { + 'ftp_incoming_port': 'test_value_3', + 'ftp_over_http': 'enable', + 'http_incoming_port': 'test_value_5', + 'https_incoming_port': 'test_value_6', + 'https_replacement_message': 'enable', + 'incoming_ip': 'test_value_8', + 'incoming_ip6': 'test_value_9', + 'ipv6_status': 'enable', + 'message_upon_server_error': 'enable', + 'outgoing_ip': 'test_value_12', + 'outgoing_ip6': 'test_value_13', + 'pac_file_data': 'test_value_14', + 'pac_file_name': 'test_value_15', + 'pac_file_server_port': 'test_value_16', + 'pac_file_server_status': 'enable', + 'pac_file_url': 'test_value_18', + 'pref_dns_result': 'ipv4', + 'realm': 'test_value_20', + 'sec_default_action': 'accept', + 'socks': 'enable', + 'socks_incoming_port': 'test_value_23', + 'ssl_algorithm': 'low', + 'status': 'enable', + 'strict_guest': 'enable', + 'trace_auth_no_rsp': 'enable', + 'unknown_http_version': 'reject' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_web_proxy_explicit.fortios_web_proxy(input_data, fos_instance) + + expected_data = { + 'ftp-incoming-port': 'test_value_3', + 'ftp-over-http': 'enable', + 'http-incoming-port': 'test_value_5', + 'https-incoming-port': 'test_value_6', + 'https-replacement-message': 'enable', + 'incoming-ip': 'test_value_8', + 'incoming-ip6': 'test_value_9', + 'ipv6-status': 'enable', + 'message-upon-server-error': 'enable', + 'outgoing-ip': 'test_value_12', + 'outgoing-ip6': 'test_value_13', + 'pac-file-data': 'test_value_14', + 'pac-file-name': 'test_value_15', + 'pac-file-server-port': 'test_value_16', + 'pac-file-server-status': 'enable', + 'pac-file-url': 'test_value_18', + 'pref-dns-result': 'ipv4', + 'realm': 'test_value_20', + 'sec-default-action': 'accept', + 'socks': 'enable', + 'socks-incoming-port': 'test_value_23', + 'ssl-algorithm': 'low', + 'status': 'enable', + 'strict-guest': 'enable', + 'trace-auth-no-rsp': 'enable', + 'unknown-http-version': 'reject' + } + + set_method_mock.assert_called_with('web-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_web_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', + 'web_proxy_explicit': { + 'random_attribute_not_valid': 'tag', + 'ftp_incoming_port': 'test_value_3', + 'ftp_over_http': 'enable', + 'http_incoming_port': 'test_value_5', + 'https_incoming_port': 'test_value_6', + 'https_replacement_message': 'enable', + 'incoming_ip': 'test_value_8', + 'incoming_ip6': 'test_value_9', + 'ipv6_status': 'enable', + 'message_upon_server_error': 'enable', + 'outgoing_ip': 'test_value_12', + 'outgoing_ip6': 'test_value_13', + 'pac_file_data': 'test_value_14', + 'pac_file_name': 'test_value_15', + 'pac_file_server_port': 'test_value_16', + 'pac_file_server_status': 'enable', + 'pac_file_url': 'test_value_18', + 'pref_dns_result': 'ipv4', + 'realm': 'test_value_20', + 'sec_default_action': 'accept', + 'socks': 'enable', + 'socks_incoming_port': 'test_value_23', + 'ssl_algorithm': 'low', + 'status': 'enable', + 'strict_guest': 'enable', + 'trace_auth_no_rsp': 'enable', + 'unknown_http_version': 'reject' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_web_proxy_explicit.fortios_web_proxy(input_data, fos_instance) + + expected_data = { + 'ftp-incoming-port': 'test_value_3', + 'ftp-over-http': 'enable', + 'http-incoming-port': 'test_value_5', + 'https-incoming-port': 'test_value_6', + 'https-replacement-message': 'enable', + 'incoming-ip': 'test_value_8', + 'incoming-ip6': 'test_value_9', + 'ipv6-status': 'enable', + 'message-upon-server-error': 'enable', + 'outgoing-ip': 'test_value_12', + 'outgoing-ip6': 'test_value_13', + 'pac-file-data': 'test_value_14', + 'pac-file-name': 'test_value_15', + 'pac-file-server-port': 'test_value_16', + 'pac-file-server-status': 'enable', + 'pac-file-url': 'test_value_18', + 'pref-dns-result': 'ipv4', + 'realm': 'test_value_20', + 'sec-default-action': 'accept', + 'socks': 'enable', + 'socks-incoming-port': 'test_value_23', + 'ssl-algorithm': 'low', + 'status': 'enable', + 'strict-guest': 'enable', + 'trace-auth-no-rsp': 'enable', + 'unknown-http-version': 'reject' + } + + set_method_mock.assert_called_with('web-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_web_proxy_global.py b/test/units/modules/network/fortios/test_fortios_web_proxy_global.py new file mode 100644 index 00000000000..24d4236eea3 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_web_proxy_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_web_proxy_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_web_proxy_global.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_web_proxy_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', + 'web_proxy_global': { + 'fast_policy_match': 'enable', + 'forward_proxy_auth': 'enable', + 'forward_server_affinity_timeout': '5', + 'learn_client_ip': 'enable', + 'learn_client_ip_from_header': 'true-client-ip', + 'max_message_length': '8', + 'max_request_length': '9', + 'max_waf_body_cache_length': '10', + 'proxy_fqdn': 'test_value_11', + 'strict_web_check': 'enable', + 'tunnel_non_http': 'enable', + 'unknown_http_version': 'reject', + 'webproxy_profile': 'test_value_15' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_web_proxy_global.fortios_web_proxy(input_data, fos_instance) + + expected_data = { + 'fast-policy-match': 'enable', + 'forward-proxy-auth': 'enable', + 'forward-server-affinity-timeout': '5', + 'learn-client-ip': 'enable', + 'learn-client-ip-from-header': 'true-client-ip', + 'max-message-length': '8', + 'max-request-length': '9', + 'max-waf-body-cache-length': '10', + 'proxy-fqdn': 'test_value_11', + 'strict-web-check': 'enable', + 'tunnel-non-http': 'enable', + 'unknown-http-version': 'reject', + 'webproxy-profile': 'test_value_15' + } + + set_method_mock.assert_called_with('web-proxy', '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_web_proxy_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', + 'web_proxy_global': { + 'fast_policy_match': 'enable', + 'forward_proxy_auth': 'enable', + 'forward_server_affinity_timeout': '5', + 'learn_client_ip': 'enable', + 'learn_client_ip_from_header': 'true-client-ip', + 'max_message_length': '8', + 'max_request_length': '9', + 'max_waf_body_cache_length': '10', + 'proxy_fqdn': 'test_value_11', + 'strict_web_check': 'enable', + 'tunnel_non_http': 'enable', + 'unknown_http_version': 'reject', + 'webproxy_profile': 'test_value_15' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_web_proxy_global.fortios_web_proxy(input_data, fos_instance) + + expected_data = { + 'fast-policy-match': 'enable', + 'forward-proxy-auth': 'enable', + 'forward-server-affinity-timeout': '5', + 'learn-client-ip': 'enable', + 'learn-client-ip-from-header': 'true-client-ip', + 'max-message-length': '8', + 'max-request-length': '9', + 'max-waf-body-cache-length': '10', + 'proxy-fqdn': 'test_value_11', + 'strict-web-check': 'enable', + 'tunnel-non-http': 'enable', + 'unknown-http-version': 'reject', + 'webproxy-profile': 'test_value_15' + } + + set_method_mock.assert_called_with('web-proxy', '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_web_proxy_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', + 'web_proxy_global': { + 'fast_policy_match': 'enable', + 'forward_proxy_auth': 'enable', + 'forward_server_affinity_timeout': '5', + 'learn_client_ip': 'enable', + 'learn_client_ip_from_header': 'true-client-ip', + 'max_message_length': '8', + 'max_request_length': '9', + 'max_waf_body_cache_length': '10', + 'proxy_fqdn': 'test_value_11', + 'strict_web_check': 'enable', + 'tunnel_non_http': 'enable', + 'unknown_http_version': 'reject', + 'webproxy_profile': 'test_value_15' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_web_proxy_global.fortios_web_proxy(input_data, fos_instance) + + expected_data = { + 'fast-policy-match': 'enable', + 'forward-proxy-auth': 'enable', + 'forward-server-affinity-timeout': '5', + 'learn-client-ip': 'enable', + 'learn-client-ip-from-header': 'true-client-ip', + 'max-message-length': '8', + 'max-request-length': '9', + 'max-waf-body-cache-length': '10', + 'proxy-fqdn': 'test_value_11', + 'strict-web-check': 'enable', + 'tunnel-non-http': 'enable', + 'unknown-http-version': 'reject', + 'webproxy-profile': 'test_value_15' + } + + set_method_mock.assert_called_with('web-proxy', '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_web_proxy_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', + 'web_proxy_global': { + 'random_attribute_not_valid': 'tag', + 'fast_policy_match': 'enable', + 'forward_proxy_auth': 'enable', + 'forward_server_affinity_timeout': '5', + 'learn_client_ip': 'enable', + 'learn_client_ip_from_header': 'true-client-ip', + 'max_message_length': '8', + 'max_request_length': '9', + 'max_waf_body_cache_length': '10', + 'proxy_fqdn': 'test_value_11', + 'strict_web_check': 'enable', + 'tunnel_non_http': 'enable', + 'unknown_http_version': 'reject', + 'webproxy_profile': 'test_value_15' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_web_proxy_global.fortios_web_proxy(input_data, fos_instance) + + expected_data = { + 'fast-policy-match': 'enable', + 'forward-proxy-auth': 'enable', + 'forward-server-affinity-timeout': '5', + 'learn-client-ip': 'enable', + 'learn-client-ip-from-header': 'true-client-ip', + 'max-message-length': '8', + 'max-request-length': '9', + 'max-waf-body-cache-length': '10', + 'proxy-fqdn': 'test_value_11', + 'strict-web-check': 'enable', + 'tunnel-non-http': 'enable', + 'unknown-http-version': 'reject', + 'webproxy-profile': 'test_value_15' + } + + set_method_mock.assert_called_with('web-proxy', '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_web_proxy_profile.py b/test/units/modules/network/fortios/test_fortios_web_proxy_profile.py new file mode 100644 index 00000000000..d401b301161 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_web_proxy_profile.py @@ -0,0 +1,289 @@ +# 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_web_proxy_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_web_proxy_profile.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_web_proxy_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', + 'web_proxy_profile': { + 'header_client_ip': 'pass', + 'header_front_end_https': 'pass', + 'header_via_request': 'pass', + 'header_via_response': 'pass', + 'header_x_authenticated_groups': 'pass', + 'header_x_authenticated_user': 'pass', + 'header_x_forwarded_for': 'pass', + 'log_header_change': 'enable', + 'name': 'default_name_11', + 'strip_encoding': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_web_proxy_profile.fortios_web_proxy(input_data, fos_instance) + + expected_data = { + 'header-client-ip': 'pass', + 'header-front-end-https': 'pass', + 'header-via-request': 'pass', + 'header-via-response': 'pass', + 'header-x-authenticated-groups': 'pass', + 'header-x-authenticated-user': 'pass', + 'header-x-forwarded-for': 'pass', + 'log-header-change': 'enable', + 'name': 'default_name_11', + 'strip-encoding': 'enable' + } + + set_method_mock.assert_called_with('web-proxy', '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_web_proxy_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', + 'web_proxy_profile': { + 'header_client_ip': 'pass', + 'header_front_end_https': 'pass', + 'header_via_request': 'pass', + 'header_via_response': 'pass', + 'header_x_authenticated_groups': 'pass', + 'header_x_authenticated_user': 'pass', + 'header_x_forwarded_for': 'pass', + 'log_header_change': 'enable', + 'name': 'default_name_11', + 'strip_encoding': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_web_proxy_profile.fortios_web_proxy(input_data, fos_instance) + + expected_data = { + 'header-client-ip': 'pass', + 'header-front-end-https': 'pass', + 'header-via-request': 'pass', + 'header-via-response': 'pass', + 'header-x-authenticated-groups': 'pass', + 'header-x-authenticated-user': 'pass', + 'header-x-forwarded-for': 'pass', + 'log-header-change': 'enable', + 'name': 'default_name_11', + 'strip-encoding': 'enable' + } + + set_method_mock.assert_called_with('web-proxy', '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_web_proxy_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', + 'web_proxy_profile': { + 'header_client_ip': 'pass', + 'header_front_end_https': 'pass', + 'header_via_request': 'pass', + 'header_via_response': 'pass', + 'header_x_authenticated_groups': 'pass', + 'header_x_authenticated_user': 'pass', + 'header_x_forwarded_for': 'pass', + 'log_header_change': 'enable', + 'name': 'default_name_11', + 'strip_encoding': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_web_proxy_profile.fortios_web_proxy(input_data, fos_instance) + + delete_method_mock.assert_called_with('web-proxy', '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_web_proxy_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', + 'web_proxy_profile': { + 'header_client_ip': 'pass', + 'header_front_end_https': 'pass', + 'header_via_request': 'pass', + 'header_via_response': 'pass', + 'header_x_authenticated_groups': 'pass', + 'header_x_authenticated_user': 'pass', + 'header_x_forwarded_for': 'pass', + 'log_header_change': 'enable', + 'name': 'default_name_11', + 'strip_encoding': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_web_proxy_profile.fortios_web_proxy(input_data, fos_instance) + + delete_method_mock.assert_called_with('web-proxy', '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_web_proxy_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', + 'web_proxy_profile': { + 'header_client_ip': 'pass', + 'header_front_end_https': 'pass', + 'header_via_request': 'pass', + 'header_via_response': 'pass', + 'header_x_authenticated_groups': 'pass', + 'header_x_authenticated_user': 'pass', + 'header_x_forwarded_for': 'pass', + 'log_header_change': 'enable', + 'name': 'default_name_11', + 'strip_encoding': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_web_proxy_profile.fortios_web_proxy(input_data, fos_instance) + + expected_data = { + 'header-client-ip': 'pass', + 'header-front-end-https': 'pass', + 'header-via-request': 'pass', + 'header-via-response': 'pass', + 'header-x-authenticated-groups': 'pass', + 'header-x-authenticated-user': 'pass', + 'header-x-forwarded-for': 'pass', + 'log-header-change': 'enable', + 'name': 'default_name_11', + 'strip-encoding': 'enable' + } + + set_method_mock.assert_called_with('web-proxy', '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_web_proxy_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', + 'web_proxy_profile': { + 'random_attribute_not_valid': 'tag', + 'header_client_ip': 'pass', + 'header_front_end_https': 'pass', + 'header_via_request': 'pass', + 'header_via_response': 'pass', + 'header_x_authenticated_groups': 'pass', + 'header_x_authenticated_user': 'pass', + 'header_x_forwarded_for': 'pass', + 'log_header_change': 'enable', + 'name': 'default_name_11', + 'strip_encoding': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_web_proxy_profile.fortios_web_proxy(input_data, fos_instance) + + expected_data = { + 'header-client-ip': 'pass', + 'header-front-end-https': 'pass', + 'header-via-request': 'pass', + 'header-via-response': 'pass', + 'header-x-authenticated-groups': 'pass', + 'header-x-authenticated-user': 'pass', + 'header-x-forwarded-for': 'pass', + 'log-header-change': 'enable', + 'name': 'default_name_11', + 'strip-encoding': 'enable' + } + + set_method_mock.assert_called_with('web-proxy', '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_webfilter_fortiguard.py b/test/units/modules/network/fortios/test_fortios_webfilter_fortiguard.py new file mode 100644 index 00000000000..f46455ec2ef --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_webfilter_fortiguard.py @@ -0,0 +1,231 @@ +# 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_webfilter_fortiguard +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_webfilter_fortiguard.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_webfilter_fortiguard_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', + 'webfilter_fortiguard': { + 'cache_mem_percent': '3', + 'cache_mode': 'ttl', + 'cache_prefix_match': 'enable', + 'close_ports': 'enable', + 'ovrd_auth_https': 'enable', + 'ovrd_auth_port': '8', + 'ovrd_auth_port_http': '9', + 'ovrd_auth_port_https': '10', + 'ovrd_auth_port_warning': '11', + 'request_packet_size_limit': '12', + 'warn_auth_https': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_fortiguard.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'cache-mem-percent': '3', + 'cache-mode': 'ttl', + 'cache-prefix-match': 'enable', + 'close-ports': 'enable', + 'ovrd-auth-https': 'enable', + 'ovrd-auth-port': '8', + 'ovrd-auth-port-http': '9', + 'ovrd-auth-port-https': '10', + 'ovrd-auth-port-warning': '11', + 'request-packet-size-limit': '12', + 'warn-auth-https': 'enable' + } + + set_method_mock.assert_called_with('webfilter', 'fortiguard', 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_webfilter_fortiguard_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', + 'webfilter_fortiguard': { + 'cache_mem_percent': '3', + 'cache_mode': 'ttl', + 'cache_prefix_match': 'enable', + 'close_ports': 'enable', + 'ovrd_auth_https': 'enable', + 'ovrd_auth_port': '8', + 'ovrd_auth_port_http': '9', + 'ovrd_auth_port_https': '10', + 'ovrd_auth_port_warning': '11', + 'request_packet_size_limit': '12', + 'warn_auth_https': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_fortiguard.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'cache-mem-percent': '3', + 'cache-mode': 'ttl', + 'cache-prefix-match': 'enable', + 'close-ports': 'enable', + 'ovrd-auth-https': 'enable', + 'ovrd-auth-port': '8', + 'ovrd-auth-port-http': '9', + 'ovrd-auth-port-https': '10', + 'ovrd-auth-port-warning': '11', + 'request-packet-size-limit': '12', + 'warn-auth-https': 'enable' + } + + set_method_mock.assert_called_with('webfilter', 'fortiguard', 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_webfilter_fortiguard_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', + 'webfilter_fortiguard': { + 'cache_mem_percent': '3', + 'cache_mode': 'ttl', + 'cache_prefix_match': 'enable', + 'close_ports': 'enable', + 'ovrd_auth_https': 'enable', + 'ovrd_auth_port': '8', + 'ovrd_auth_port_http': '9', + 'ovrd_auth_port_https': '10', + 'ovrd_auth_port_warning': '11', + 'request_packet_size_limit': '12', + 'warn_auth_https': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_fortiguard.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'cache-mem-percent': '3', + 'cache-mode': 'ttl', + 'cache-prefix-match': 'enable', + 'close-ports': 'enable', + 'ovrd-auth-https': 'enable', + 'ovrd-auth-port': '8', + 'ovrd-auth-port-http': '9', + 'ovrd-auth-port-https': '10', + 'ovrd-auth-port-warning': '11', + 'request-packet-size-limit': '12', + 'warn-auth-https': 'enable' + } + + set_method_mock.assert_called_with('webfilter', 'fortiguard', 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_webfilter_fortiguard_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', + 'webfilter_fortiguard': { + 'random_attribute_not_valid': 'tag', + 'cache_mem_percent': '3', + 'cache_mode': 'ttl', + 'cache_prefix_match': 'enable', + 'close_ports': 'enable', + 'ovrd_auth_https': 'enable', + 'ovrd_auth_port': '8', + 'ovrd_auth_port_http': '9', + 'ovrd_auth_port_https': '10', + 'ovrd_auth_port_warning': '11', + 'request_packet_size_limit': '12', + 'warn_auth_https': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_fortiguard.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'cache-mem-percent': '3', + 'cache-mode': 'ttl', + 'cache-prefix-match': 'enable', + 'close-ports': 'enable', + 'ovrd-auth-https': 'enable', + 'ovrd-auth-port': '8', + 'ovrd-auth-port-http': '9', + 'ovrd-auth-port-https': '10', + 'ovrd-auth-port-warning': '11', + 'request-packet-size-limit': '12', + 'warn-auth-https': 'enable' + } + + set_method_mock.assert_called_with('webfilter', 'fortiguard', 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_webfilter_ftgd_local_cat.py b/test/units/modules/network/fortios/test_fortios_webfilter_ftgd_local_cat.py new file mode 100644 index 00000000000..ae1374a4009 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_webfilter_ftgd_local_cat.py @@ -0,0 +1,219 @@ +# 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_webfilter_ftgd_local_cat +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_webfilter_ftgd_local_cat.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_webfilter_ftgd_local_cat_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', + 'webfilter_ftgd_local_cat': { + 'desc': 'test_value_3', + 'id': '4', + 'status': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ftgd_local_cat.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'desc': 'test_value_3', + 'id': '4', + 'status': 'enable' + } + + set_method_mock.assert_called_with('webfilter', 'ftgd-local-cat', 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_webfilter_ftgd_local_cat_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', + 'webfilter_ftgd_local_cat': { + 'desc': 'test_value_3', + 'id': '4', + 'status': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ftgd_local_cat.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'desc': 'test_value_3', + 'id': '4', + 'status': 'enable' + } + + set_method_mock.assert_called_with('webfilter', 'ftgd-local-cat', 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_webfilter_ftgd_local_cat_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', + 'webfilter_ftgd_local_cat': { + 'desc': 'test_value_3', + 'id': '4', + 'status': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ftgd_local_cat.fortios_webfilter(input_data, fos_instance) + + delete_method_mock.assert_called_with('webfilter', 'ftgd-local-cat', 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_webfilter_ftgd_local_cat_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', + 'webfilter_ftgd_local_cat': { + 'desc': 'test_value_3', + 'id': '4', + 'status': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ftgd_local_cat.fortios_webfilter(input_data, fos_instance) + + delete_method_mock.assert_called_with('webfilter', 'ftgd-local-cat', 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_webfilter_ftgd_local_cat_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', + 'webfilter_ftgd_local_cat': { + 'desc': 'test_value_3', + 'id': '4', + 'status': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ftgd_local_cat.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'desc': 'test_value_3', + 'id': '4', + 'status': 'enable' + } + + set_method_mock.assert_called_with('webfilter', 'ftgd-local-cat', 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_webfilter_ftgd_local_cat_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', + 'webfilter_ftgd_local_cat': { + 'random_attribute_not_valid': 'tag', + 'desc': 'test_value_3', + 'id': '4', + 'status': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ftgd_local_cat.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'desc': 'test_value_3', + 'id': '4', + 'status': 'enable' + } + + set_method_mock.assert_called_with('webfilter', 'ftgd-local-cat', 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_webfilter_ftgd_local_rating.py b/test/units/modules/network/fortios/test_fortios_webfilter_ftgd_local_rating.py new file mode 100644 index 00000000000..377ada09587 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_webfilter_ftgd_local_rating.py @@ -0,0 +1,219 @@ +# 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_webfilter_ftgd_local_rating +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_webfilter_ftgd_local_rating.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_webfilter_ftgd_local_rating_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', + 'webfilter_ftgd_local_rating': { + 'rating': 'test_value_3', + 'status': 'enable', + 'url': 'myurl_5.com' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ftgd_local_rating.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'rating': 'test_value_3', + 'status': 'enable', + 'url': 'myurl_5.com' + } + + set_method_mock.assert_called_with('webfilter', 'ftgd-local-rating', 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_webfilter_ftgd_local_rating_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', + 'webfilter_ftgd_local_rating': { + 'rating': 'test_value_3', + 'status': 'enable', + 'url': 'myurl_5.com' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ftgd_local_rating.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'rating': 'test_value_3', + 'status': 'enable', + 'url': 'myurl_5.com' + } + + set_method_mock.assert_called_with('webfilter', 'ftgd-local-rating', 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_webfilter_ftgd_local_rating_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', + 'webfilter_ftgd_local_rating': { + 'rating': 'test_value_3', + 'status': 'enable', + 'url': 'myurl_5.com' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ftgd_local_rating.fortios_webfilter(input_data, fos_instance) + + delete_method_mock.assert_called_with('webfilter', 'ftgd-local-rating', 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_webfilter_ftgd_local_rating_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', + 'webfilter_ftgd_local_rating': { + 'rating': 'test_value_3', + 'status': 'enable', + 'url': 'myurl_5.com' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ftgd_local_rating.fortios_webfilter(input_data, fos_instance) + + delete_method_mock.assert_called_with('webfilter', 'ftgd-local-rating', 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_webfilter_ftgd_local_rating_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', + 'webfilter_ftgd_local_rating': { + 'rating': 'test_value_3', + 'status': 'enable', + 'url': 'myurl_5.com' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ftgd_local_rating.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'rating': 'test_value_3', + 'status': 'enable', + 'url': 'myurl_5.com' + } + + set_method_mock.assert_called_with('webfilter', 'ftgd-local-rating', 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_webfilter_ftgd_local_rating_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', + 'webfilter_ftgd_local_rating': { + 'random_attribute_not_valid': 'tag', + 'rating': 'test_value_3', + 'status': 'enable', + 'url': 'myurl_5.com' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ftgd_local_rating.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'rating': 'test_value_3', + 'status': 'enable', + 'url': 'myurl_5.com' + } + + set_method_mock.assert_called_with('webfilter', 'ftgd-local-rating', 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_webfilter_ips_urlfilter_cache_setting.py b/test/units/modules/network/fortios/test_fortios_webfilter_ips_urlfilter_cache_setting.py new file mode 100644 index 00000000000..688afd3eddc --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_webfilter_ips_urlfilter_cache_setting.py @@ -0,0 +1,159 @@ +# 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_webfilter_ips_urlfilter_cache_setting +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_webfilter_ips_urlfilter_cache_setting.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_webfilter_ips_urlfilter_cache_setting_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', + 'webfilter_ips_urlfilter_cache_setting': { + 'dns_retry_interval': '3', + 'extended_ttl': '4' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ips_urlfilter_cache_setting.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'dns-retry-interval': '3', + 'extended-ttl': '4' + } + + set_method_mock.assert_called_with('webfilter', 'ips-urlfilter-cache-setting', 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_webfilter_ips_urlfilter_cache_setting_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', + 'webfilter_ips_urlfilter_cache_setting': { + 'dns_retry_interval': '3', + 'extended_ttl': '4' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ips_urlfilter_cache_setting.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'dns-retry-interval': '3', + 'extended-ttl': '4' + } + + set_method_mock.assert_called_with('webfilter', 'ips-urlfilter-cache-setting', 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_webfilter_ips_urlfilter_cache_setting_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', + 'webfilter_ips_urlfilter_cache_setting': { + 'dns_retry_interval': '3', + 'extended_ttl': '4' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ips_urlfilter_cache_setting.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'dns-retry-interval': '3', + 'extended-ttl': '4' + } + + set_method_mock.assert_called_with('webfilter', 'ips-urlfilter-cache-setting', 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_webfilter_ips_urlfilter_cache_setting_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', + 'webfilter_ips_urlfilter_cache_setting': { + 'random_attribute_not_valid': 'tag', + 'dns_retry_interval': '3', + 'extended_ttl': '4' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ips_urlfilter_cache_setting.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'dns-retry-interval': '3', + 'extended-ttl': '4' + } + + set_method_mock.assert_called_with('webfilter', 'ips-urlfilter-cache-setting', 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_webfilter_ips_urlfilter_setting.py b/test/units/modules/network/fortios/test_fortios_webfilter_ips_urlfilter_setting.py new file mode 100644 index 00000000000..d79a65760c6 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_webfilter_ips_urlfilter_setting.py @@ -0,0 +1,175 @@ +# 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_webfilter_ips_urlfilter_setting +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_webfilter_ips_urlfilter_setting.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_webfilter_ips_urlfilter_setting_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', + 'webfilter_ips_urlfilter_setting': { + 'device': 'test_value_3', + 'distance': '4', + 'gateway': 'test_value_5', + 'geo_filter': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ips_urlfilter_setting.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'device': 'test_value_3', + 'distance': '4', + 'gateway': 'test_value_5', + 'geo-filter': 'test_value_6' + } + + set_method_mock.assert_called_with('webfilter', 'ips-urlfilter-setting', 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_webfilter_ips_urlfilter_setting_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', + 'webfilter_ips_urlfilter_setting': { + 'device': 'test_value_3', + 'distance': '4', + 'gateway': 'test_value_5', + 'geo_filter': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ips_urlfilter_setting.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'device': 'test_value_3', + 'distance': '4', + 'gateway': 'test_value_5', + 'geo-filter': 'test_value_6' + } + + set_method_mock.assert_called_with('webfilter', 'ips-urlfilter-setting', 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_webfilter_ips_urlfilter_setting_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', + 'webfilter_ips_urlfilter_setting': { + 'device': 'test_value_3', + 'distance': '4', + 'gateway': 'test_value_5', + 'geo_filter': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ips_urlfilter_setting.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'device': 'test_value_3', + 'distance': '4', + 'gateway': 'test_value_5', + 'geo-filter': 'test_value_6' + } + + set_method_mock.assert_called_with('webfilter', 'ips-urlfilter-setting', 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_webfilter_ips_urlfilter_setting_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', + 'webfilter_ips_urlfilter_setting': { + 'random_attribute_not_valid': 'tag', + 'device': 'test_value_3', + 'distance': '4', + 'gateway': 'test_value_5', + 'geo_filter': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ips_urlfilter_setting.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'device': 'test_value_3', + 'distance': '4', + 'gateway': 'test_value_5', + 'geo-filter': 'test_value_6' + } + + set_method_mock.assert_called_with('webfilter', 'ips-urlfilter-setting', 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_webfilter_ips_urlfilter_setting6.py b/test/units/modules/network/fortios/test_fortios_webfilter_ips_urlfilter_setting6.py new file mode 100644 index 00000000000..32b70063918 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_webfilter_ips_urlfilter_setting6.py @@ -0,0 +1,175 @@ +# 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_webfilter_ips_urlfilter_setting6 +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_webfilter_ips_urlfilter_setting6.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_webfilter_ips_urlfilter_setting6_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', + 'webfilter_ips_urlfilter_setting6': { + 'device': 'test_value_3', + 'distance': '4', + 'gateway6': 'test_value_5', + 'geo_filter': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ips_urlfilter_setting6.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'device': 'test_value_3', + 'distance': '4', + 'gateway6': 'test_value_5', + 'geo-filter': 'test_value_6' + } + + set_method_mock.assert_called_with('webfilter', 'ips-urlfilter-setting6', 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_webfilter_ips_urlfilter_setting6_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', + 'webfilter_ips_urlfilter_setting6': { + 'device': 'test_value_3', + 'distance': '4', + 'gateway6': 'test_value_5', + 'geo_filter': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ips_urlfilter_setting6.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'device': 'test_value_3', + 'distance': '4', + 'gateway6': 'test_value_5', + 'geo-filter': 'test_value_6' + } + + set_method_mock.assert_called_with('webfilter', 'ips-urlfilter-setting6', 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_webfilter_ips_urlfilter_setting6_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', + 'webfilter_ips_urlfilter_setting6': { + 'device': 'test_value_3', + 'distance': '4', + 'gateway6': 'test_value_5', + 'geo_filter': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ips_urlfilter_setting6.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'device': 'test_value_3', + 'distance': '4', + 'gateway6': 'test_value_5', + 'geo-filter': 'test_value_6' + } + + set_method_mock.assert_called_with('webfilter', 'ips-urlfilter-setting6', 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_webfilter_ips_urlfilter_setting6_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', + 'webfilter_ips_urlfilter_setting6': { + 'random_attribute_not_valid': 'tag', + 'device': 'test_value_3', + 'distance': '4', + 'gateway6': 'test_value_5', + 'geo_filter': 'test_value_6' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_ips_urlfilter_setting6.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'device': 'test_value_3', + 'distance': '4', + 'gateway6': 'test_value_5', + 'geo-filter': 'test_value_6' + } + + set_method_mock.assert_called_with('webfilter', 'ips-urlfilter-setting6', 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_webfilter_override.py b/test/units/modules/network/fortios/test_fortios_webfilter_override.py new file mode 100644 index 00000000000..0594217dc71 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_webfilter_override.py @@ -0,0 +1,299 @@ +# 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_webfilter_override +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_webfilter_override.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_webfilter_override_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', + 'webfilter_override': { + 'expires': 'test_value_3', + 'id': '4', + 'initiator': 'test_value_5', + 'ip': 'test_value_6', + 'ip6': 'test_value_7', + 'new_profile': 'test_value_8', + 'old_profile': 'test_value_9', + 'scope': 'user', + 'status': 'enable', + 'user': 'test_value_12', + 'user_group': 'test_value_13' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_override.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'expires': 'test_value_3', + 'id': '4', + 'initiator': 'test_value_5', + 'ip': 'test_value_6', + 'ip6': 'test_value_7', + 'new-profile': 'test_value_8', + 'old-profile': 'test_value_9', + 'scope': 'user', + 'status': 'enable', + 'user': 'test_value_12', + 'user-group': 'test_value_13' + } + + set_method_mock.assert_called_with('webfilter', 'override', 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_webfilter_override_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', + 'webfilter_override': { + 'expires': 'test_value_3', + 'id': '4', + 'initiator': 'test_value_5', + 'ip': 'test_value_6', + 'ip6': 'test_value_7', + 'new_profile': 'test_value_8', + 'old_profile': 'test_value_9', + 'scope': 'user', + 'status': 'enable', + 'user': 'test_value_12', + 'user_group': 'test_value_13' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_override.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'expires': 'test_value_3', + 'id': '4', + 'initiator': 'test_value_5', + 'ip': 'test_value_6', + 'ip6': 'test_value_7', + 'new-profile': 'test_value_8', + 'old-profile': 'test_value_9', + 'scope': 'user', + 'status': 'enable', + 'user': 'test_value_12', + 'user-group': 'test_value_13' + } + + set_method_mock.assert_called_with('webfilter', 'override', 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_webfilter_override_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', + 'webfilter_override': { + 'expires': 'test_value_3', + 'id': '4', + 'initiator': 'test_value_5', + 'ip': 'test_value_6', + 'ip6': 'test_value_7', + 'new_profile': 'test_value_8', + 'old_profile': 'test_value_9', + 'scope': 'user', + 'status': 'enable', + 'user': 'test_value_12', + 'user_group': 'test_value_13' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_override.fortios_webfilter(input_data, fos_instance) + + delete_method_mock.assert_called_with('webfilter', 'override', 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_webfilter_override_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', + 'webfilter_override': { + 'expires': 'test_value_3', + 'id': '4', + 'initiator': 'test_value_5', + 'ip': 'test_value_6', + 'ip6': 'test_value_7', + 'new_profile': 'test_value_8', + 'old_profile': 'test_value_9', + 'scope': 'user', + 'status': 'enable', + 'user': 'test_value_12', + 'user_group': 'test_value_13' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_override.fortios_webfilter(input_data, fos_instance) + + delete_method_mock.assert_called_with('webfilter', 'override', 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_webfilter_override_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', + 'webfilter_override': { + 'expires': 'test_value_3', + 'id': '4', + 'initiator': 'test_value_5', + 'ip': 'test_value_6', + 'ip6': 'test_value_7', + 'new_profile': 'test_value_8', + 'old_profile': 'test_value_9', + 'scope': 'user', + 'status': 'enable', + 'user': 'test_value_12', + 'user_group': 'test_value_13' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_override.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'expires': 'test_value_3', + 'id': '4', + 'initiator': 'test_value_5', + 'ip': 'test_value_6', + 'ip6': 'test_value_7', + 'new-profile': 'test_value_8', + 'old-profile': 'test_value_9', + 'scope': 'user', + 'status': 'enable', + 'user': 'test_value_12', + 'user-group': 'test_value_13' + } + + set_method_mock.assert_called_with('webfilter', 'override', 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_webfilter_override_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', + 'webfilter_override': { + 'random_attribute_not_valid': 'tag', + 'expires': 'test_value_3', + 'id': '4', + 'initiator': 'test_value_5', + 'ip': 'test_value_6', + 'ip6': 'test_value_7', + 'new_profile': 'test_value_8', + 'old_profile': 'test_value_9', + 'scope': 'user', + 'status': 'enable', + 'user': 'test_value_12', + 'user_group': 'test_value_13' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_override.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'expires': 'test_value_3', + 'id': '4', + 'initiator': 'test_value_5', + 'ip': 'test_value_6', + 'ip6': 'test_value_7', + 'new-profile': 'test_value_8', + 'old-profile': 'test_value_9', + 'scope': 'user', + 'status': 'enable', + 'user': 'test_value_12', + 'user-group': 'test_value_13' + } + + set_method_mock.assert_called_with('webfilter', 'override', 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_webfilter_profile.py b/test/units/modules/network/fortios/test_fortios_webfilter_profile.py new file mode 100644 index 00000000000..07efbfcbd52 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_webfilter_profile.py @@ -0,0 +1,479 @@ +# 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_webfilter_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_webfilter_profile.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_webfilter_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', + 'webfilter_profile': { + 'comment': 'Optional comments.', + 'extended_log': 'enable', + 'https_replacemsg': 'enable', + 'inspection_mode': 'proxy', + 'log_all_url': 'enable', + 'name': 'default_name_8', + 'options': 'activexfilter', + 'ovrd_perm': 'bannedword-override', + 'post_action': 'normal', + 'replacemsg_group': 'test_value_12', + 'web_content_log': 'enable', + 'web_extended_all_action_log': 'enable', + 'web_filter_activex_log': 'enable', + 'web_filter_applet_log': 'enable', + 'web_filter_command_block_log': 'enable', + 'web_filter_cookie_log': 'enable', + 'web_filter_cookie_removal_log': 'enable', + 'web_filter_js_log': 'enable', + 'web_filter_jscript_log': 'enable', + 'web_filter_referer_log': 'enable', + 'web_filter_unknown_log': 'enable', + 'web_filter_vbs_log': 'enable', + 'web_ftgd_err_log': 'enable', + 'web_ftgd_quota_usage': 'enable', + 'web_invalid_domain_log': 'enable', + 'web_url_log': 'enable', + 'wisp': 'enable', + 'wisp_algorithm': 'primary-secondary', + 'youtube_channel_status': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_profile.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'comment': 'Optional comments.', + 'extended-log': 'enable', + 'https-replacemsg': 'enable', + 'inspection-mode': 'proxy', + 'log-all-url': 'enable', + 'name': 'default_name_8', + 'options': 'activexfilter', + 'ovrd-perm': 'bannedword-override', + 'post-action': 'normal', + 'replacemsg-group': 'test_value_12', + 'web-content-log': 'enable', + 'web-extended-all-action-log': 'enable', + 'web-filter-activex-log': 'enable', + 'web-filter-applet-log': 'enable', + 'web-filter-command-block-log': 'enable', + 'web-filter-cookie-log': 'enable', + 'web-filter-cookie-removal-log': 'enable', + 'web-filter-js-log': 'enable', + 'web-filter-jscript-log': 'enable', + 'web-filter-referer-log': 'enable', + 'web-filter-unknown-log': 'enable', + 'web-filter-vbs-log': 'enable', + 'web-ftgd-err-log': 'enable', + 'web-ftgd-quota-usage': 'enable', + 'web-invalid-domain-log': 'enable', + 'web-url-log': 'enable', + 'wisp': 'enable', + 'wisp-algorithm': 'primary-secondary', + 'youtube-channel-status': 'disable' + } + + set_method_mock.assert_called_with('webfilter', '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_webfilter_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', + 'webfilter_profile': { + 'comment': 'Optional comments.', + 'extended_log': 'enable', + 'https_replacemsg': 'enable', + 'inspection_mode': 'proxy', + 'log_all_url': 'enable', + 'name': 'default_name_8', + 'options': 'activexfilter', + 'ovrd_perm': 'bannedword-override', + 'post_action': 'normal', + 'replacemsg_group': 'test_value_12', + 'web_content_log': 'enable', + 'web_extended_all_action_log': 'enable', + 'web_filter_activex_log': 'enable', + 'web_filter_applet_log': 'enable', + 'web_filter_command_block_log': 'enable', + 'web_filter_cookie_log': 'enable', + 'web_filter_cookie_removal_log': 'enable', + 'web_filter_js_log': 'enable', + 'web_filter_jscript_log': 'enable', + 'web_filter_referer_log': 'enable', + 'web_filter_unknown_log': 'enable', + 'web_filter_vbs_log': 'enable', + 'web_ftgd_err_log': 'enable', + 'web_ftgd_quota_usage': 'enable', + 'web_invalid_domain_log': 'enable', + 'web_url_log': 'enable', + 'wisp': 'enable', + 'wisp_algorithm': 'primary-secondary', + 'youtube_channel_status': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_profile.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'comment': 'Optional comments.', + 'extended-log': 'enable', + 'https-replacemsg': 'enable', + 'inspection-mode': 'proxy', + 'log-all-url': 'enable', + 'name': 'default_name_8', + 'options': 'activexfilter', + 'ovrd-perm': 'bannedword-override', + 'post-action': 'normal', + 'replacemsg-group': 'test_value_12', + 'web-content-log': 'enable', + 'web-extended-all-action-log': 'enable', + 'web-filter-activex-log': 'enable', + 'web-filter-applet-log': 'enable', + 'web-filter-command-block-log': 'enable', + 'web-filter-cookie-log': 'enable', + 'web-filter-cookie-removal-log': 'enable', + 'web-filter-js-log': 'enable', + 'web-filter-jscript-log': 'enable', + 'web-filter-referer-log': 'enable', + 'web-filter-unknown-log': 'enable', + 'web-filter-vbs-log': 'enable', + 'web-ftgd-err-log': 'enable', + 'web-ftgd-quota-usage': 'enable', + 'web-invalid-domain-log': 'enable', + 'web-url-log': 'enable', + 'wisp': 'enable', + 'wisp-algorithm': 'primary-secondary', + 'youtube-channel-status': 'disable' + } + + set_method_mock.assert_called_with('webfilter', '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_webfilter_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', + 'webfilter_profile': { + 'comment': 'Optional comments.', + 'extended_log': 'enable', + 'https_replacemsg': 'enable', + 'inspection_mode': 'proxy', + 'log_all_url': 'enable', + 'name': 'default_name_8', + 'options': 'activexfilter', + 'ovrd_perm': 'bannedword-override', + 'post_action': 'normal', + 'replacemsg_group': 'test_value_12', + 'web_content_log': 'enable', + 'web_extended_all_action_log': 'enable', + 'web_filter_activex_log': 'enable', + 'web_filter_applet_log': 'enable', + 'web_filter_command_block_log': 'enable', + 'web_filter_cookie_log': 'enable', + 'web_filter_cookie_removal_log': 'enable', + 'web_filter_js_log': 'enable', + 'web_filter_jscript_log': 'enable', + 'web_filter_referer_log': 'enable', + 'web_filter_unknown_log': 'enable', + 'web_filter_vbs_log': 'enable', + 'web_ftgd_err_log': 'enable', + 'web_ftgd_quota_usage': 'enable', + 'web_invalid_domain_log': 'enable', + 'web_url_log': 'enable', + 'wisp': 'enable', + 'wisp_algorithm': 'primary-secondary', + 'youtube_channel_status': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_profile.fortios_webfilter(input_data, fos_instance) + + delete_method_mock.assert_called_with('webfilter', '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_webfilter_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', + 'webfilter_profile': { + 'comment': 'Optional comments.', + 'extended_log': 'enable', + 'https_replacemsg': 'enable', + 'inspection_mode': 'proxy', + 'log_all_url': 'enable', + 'name': 'default_name_8', + 'options': 'activexfilter', + 'ovrd_perm': 'bannedword-override', + 'post_action': 'normal', + 'replacemsg_group': 'test_value_12', + 'web_content_log': 'enable', + 'web_extended_all_action_log': 'enable', + 'web_filter_activex_log': 'enable', + 'web_filter_applet_log': 'enable', + 'web_filter_command_block_log': 'enable', + 'web_filter_cookie_log': 'enable', + 'web_filter_cookie_removal_log': 'enable', + 'web_filter_js_log': 'enable', + 'web_filter_jscript_log': 'enable', + 'web_filter_referer_log': 'enable', + 'web_filter_unknown_log': 'enable', + 'web_filter_vbs_log': 'enable', + 'web_ftgd_err_log': 'enable', + 'web_ftgd_quota_usage': 'enable', + 'web_invalid_domain_log': 'enable', + 'web_url_log': 'enable', + 'wisp': 'enable', + 'wisp_algorithm': 'primary-secondary', + 'youtube_channel_status': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_profile.fortios_webfilter(input_data, fos_instance) + + delete_method_mock.assert_called_with('webfilter', '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_webfilter_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', + 'webfilter_profile': { + 'comment': 'Optional comments.', + 'extended_log': 'enable', + 'https_replacemsg': 'enable', + 'inspection_mode': 'proxy', + 'log_all_url': 'enable', + 'name': 'default_name_8', + 'options': 'activexfilter', + 'ovrd_perm': 'bannedword-override', + 'post_action': 'normal', + 'replacemsg_group': 'test_value_12', + 'web_content_log': 'enable', + 'web_extended_all_action_log': 'enable', + 'web_filter_activex_log': 'enable', + 'web_filter_applet_log': 'enable', + 'web_filter_command_block_log': 'enable', + 'web_filter_cookie_log': 'enable', + 'web_filter_cookie_removal_log': 'enable', + 'web_filter_js_log': 'enable', + 'web_filter_jscript_log': 'enable', + 'web_filter_referer_log': 'enable', + 'web_filter_unknown_log': 'enable', + 'web_filter_vbs_log': 'enable', + 'web_ftgd_err_log': 'enable', + 'web_ftgd_quota_usage': 'enable', + 'web_invalid_domain_log': 'enable', + 'web_url_log': 'enable', + 'wisp': 'enable', + 'wisp_algorithm': 'primary-secondary', + 'youtube_channel_status': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_profile.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'comment': 'Optional comments.', + 'extended-log': 'enable', + 'https-replacemsg': 'enable', + 'inspection-mode': 'proxy', + 'log-all-url': 'enable', + 'name': 'default_name_8', + 'options': 'activexfilter', + 'ovrd-perm': 'bannedword-override', + 'post-action': 'normal', + 'replacemsg-group': 'test_value_12', + 'web-content-log': 'enable', + 'web-extended-all-action-log': 'enable', + 'web-filter-activex-log': 'enable', + 'web-filter-applet-log': 'enable', + 'web-filter-command-block-log': 'enable', + 'web-filter-cookie-log': 'enable', + 'web-filter-cookie-removal-log': 'enable', + 'web-filter-js-log': 'enable', + 'web-filter-jscript-log': 'enable', + 'web-filter-referer-log': 'enable', + 'web-filter-unknown-log': 'enable', + 'web-filter-vbs-log': 'enable', + 'web-ftgd-err-log': 'enable', + 'web-ftgd-quota-usage': 'enable', + 'web-invalid-domain-log': 'enable', + 'web-url-log': 'enable', + 'wisp': 'enable', + 'wisp-algorithm': 'primary-secondary', + 'youtube-channel-status': 'disable' + } + + set_method_mock.assert_called_with('webfilter', '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_webfilter_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', + 'webfilter_profile': { + 'random_attribute_not_valid': 'tag', + 'comment': 'Optional comments.', + 'extended_log': 'enable', + 'https_replacemsg': 'enable', + 'inspection_mode': 'proxy', + 'log_all_url': 'enable', + 'name': 'default_name_8', + 'options': 'activexfilter', + 'ovrd_perm': 'bannedword-override', + 'post_action': 'normal', + 'replacemsg_group': 'test_value_12', + 'web_content_log': 'enable', + 'web_extended_all_action_log': 'enable', + 'web_filter_activex_log': 'enable', + 'web_filter_applet_log': 'enable', + 'web_filter_command_block_log': 'enable', + 'web_filter_cookie_log': 'enable', + 'web_filter_cookie_removal_log': 'enable', + 'web_filter_js_log': 'enable', + 'web_filter_jscript_log': 'enable', + 'web_filter_referer_log': 'enable', + 'web_filter_unknown_log': 'enable', + 'web_filter_vbs_log': 'enable', + 'web_ftgd_err_log': 'enable', + 'web_ftgd_quota_usage': 'enable', + 'web_invalid_domain_log': 'enable', + 'web_url_log': 'enable', + 'wisp': 'enable', + 'wisp_algorithm': 'primary-secondary', + 'youtube_channel_status': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_profile.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'comment': 'Optional comments.', + 'extended-log': 'enable', + 'https-replacemsg': 'enable', + 'inspection-mode': 'proxy', + 'log-all-url': 'enable', + 'name': 'default_name_8', + 'options': 'activexfilter', + 'ovrd-perm': 'bannedword-override', + 'post-action': 'normal', + 'replacemsg-group': 'test_value_12', + 'web-content-log': 'enable', + 'web-extended-all-action-log': 'enable', + 'web-filter-activex-log': 'enable', + 'web-filter-applet-log': 'enable', + 'web-filter-command-block-log': 'enable', + 'web-filter-cookie-log': 'enable', + 'web-filter-cookie-removal-log': 'enable', + 'web-filter-js-log': 'enable', + 'web-filter-jscript-log': 'enable', + 'web-filter-referer-log': 'enable', + 'web-filter-unknown-log': 'enable', + 'web-filter-vbs-log': 'enable', + 'web-ftgd-err-log': 'enable', + 'web-ftgd-quota-usage': 'enable', + 'web-invalid-domain-log': 'enable', + 'web-url-log': 'enable', + 'wisp': 'enable', + 'wisp-algorithm': 'primary-secondary', + 'youtube-channel-status': 'disable' + } + + set_method_mock.assert_called_with('webfilter', '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_webfilter_search_engine.py b/test/units/modules/network/fortios/test_fortios_webfilter_search_engine.py new file mode 100644 index 00000000000..ef6cd8a47a0 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_webfilter_search_engine.py @@ -0,0 +1,259 @@ +# 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_webfilter_search_engine +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_webfilter_search_engine.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_webfilter_search_engine_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', + 'webfilter_search_engine': { + 'charset': 'utf-8', + 'hostname': 'myhostname4', + 'name': 'default_name_5', + 'query': 'test_value_6', + 'safesearch': 'disable', + 'safesearch_str': 'test_value_8', + 'url': 'myurl_9.com' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_search_engine.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'charset': 'utf-8', + 'hostname': 'myhostname4', + 'name': 'default_name_5', + 'query': 'test_value_6', + 'safesearch': 'disable', + 'safesearch-str': 'test_value_8', + 'url': 'myurl_9.com' + } + + set_method_mock.assert_called_with('webfilter', 'search-engine', 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_webfilter_search_engine_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', + 'webfilter_search_engine': { + 'charset': 'utf-8', + 'hostname': 'myhostname4', + 'name': 'default_name_5', + 'query': 'test_value_6', + 'safesearch': 'disable', + 'safesearch_str': 'test_value_8', + 'url': 'myurl_9.com' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_search_engine.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'charset': 'utf-8', + 'hostname': 'myhostname4', + 'name': 'default_name_5', + 'query': 'test_value_6', + 'safesearch': 'disable', + 'safesearch-str': 'test_value_8', + 'url': 'myurl_9.com' + } + + set_method_mock.assert_called_with('webfilter', 'search-engine', 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_webfilter_search_engine_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', + 'webfilter_search_engine': { + 'charset': 'utf-8', + 'hostname': 'myhostname4', + 'name': 'default_name_5', + 'query': 'test_value_6', + 'safesearch': 'disable', + 'safesearch_str': 'test_value_8', + 'url': 'myurl_9.com' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_search_engine.fortios_webfilter(input_data, fos_instance) + + delete_method_mock.assert_called_with('webfilter', 'search-engine', 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_webfilter_search_engine_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', + 'webfilter_search_engine': { + 'charset': 'utf-8', + 'hostname': 'myhostname4', + 'name': 'default_name_5', + 'query': 'test_value_6', + 'safesearch': 'disable', + 'safesearch_str': 'test_value_8', + 'url': 'myurl_9.com' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_search_engine.fortios_webfilter(input_data, fos_instance) + + delete_method_mock.assert_called_with('webfilter', 'search-engine', 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_webfilter_search_engine_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', + 'webfilter_search_engine': { + 'charset': 'utf-8', + 'hostname': 'myhostname4', + 'name': 'default_name_5', + 'query': 'test_value_6', + 'safesearch': 'disable', + 'safesearch_str': 'test_value_8', + 'url': 'myurl_9.com' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_search_engine.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'charset': 'utf-8', + 'hostname': 'myhostname4', + 'name': 'default_name_5', + 'query': 'test_value_6', + 'safesearch': 'disable', + 'safesearch-str': 'test_value_8', + 'url': 'myurl_9.com' + } + + set_method_mock.assert_called_with('webfilter', 'search-engine', 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_webfilter_search_engine_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', + 'webfilter_search_engine': { + 'random_attribute_not_valid': 'tag', + 'charset': 'utf-8', + 'hostname': 'myhostname4', + 'name': 'default_name_5', + 'query': 'test_value_6', + 'safesearch': 'disable', + 'safesearch_str': 'test_value_8', + 'url': 'myurl_9.com' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_search_engine.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'charset': 'utf-8', + 'hostname': 'myhostname4', + 'name': 'default_name_5', + 'query': 'test_value_6', + 'safesearch': 'disable', + 'safesearch-str': 'test_value_8', + 'url': 'myurl_9.com' + } + + set_method_mock.assert_called_with('webfilter', 'search-engine', 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_webfilter_urlfilter.py b/test/units/modules/network/fortios/test_fortios_webfilter_urlfilter.py new file mode 100644 index 00000000000..2272b0af7c2 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_webfilter_urlfilter.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_webfilter_urlfilter +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_webfilter_urlfilter.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_webfilter_urlfilter_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', + 'webfilter_urlfilter': { + 'comment': 'Optional comments.', + 'id': '4', + 'ip_addr_block': 'enable', + 'name': 'default_name_6', + 'one_arm_ips_urlfilter': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_urlfilter.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'comment': 'Optional comments.', + 'id': '4', + 'ip-addr-block': 'enable', + 'name': 'default_name_6', + 'one-arm-ips-urlfilter': 'enable' + } + + set_method_mock.assert_called_with('webfilter', 'urlfilter', 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_webfilter_urlfilter_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', + 'webfilter_urlfilter': { + 'comment': 'Optional comments.', + 'id': '4', + 'ip_addr_block': 'enable', + 'name': 'default_name_6', + 'one_arm_ips_urlfilter': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_urlfilter.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'comment': 'Optional comments.', + 'id': '4', + 'ip-addr-block': 'enable', + 'name': 'default_name_6', + 'one-arm-ips-urlfilter': 'enable' + } + + set_method_mock.assert_called_with('webfilter', 'urlfilter', 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_webfilter_urlfilter_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', + 'webfilter_urlfilter': { + 'comment': 'Optional comments.', + 'id': '4', + 'ip_addr_block': 'enable', + 'name': 'default_name_6', + 'one_arm_ips_urlfilter': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_urlfilter.fortios_webfilter(input_data, fos_instance) + + delete_method_mock.assert_called_with('webfilter', 'urlfilter', 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_webfilter_urlfilter_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', + 'webfilter_urlfilter': { + 'comment': 'Optional comments.', + 'id': '4', + 'ip_addr_block': 'enable', + 'name': 'default_name_6', + 'one_arm_ips_urlfilter': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_urlfilter.fortios_webfilter(input_data, fos_instance) + + delete_method_mock.assert_called_with('webfilter', 'urlfilter', 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_webfilter_urlfilter_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', + 'webfilter_urlfilter': { + 'comment': 'Optional comments.', + 'id': '4', + 'ip_addr_block': 'enable', + 'name': 'default_name_6', + 'one_arm_ips_urlfilter': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_urlfilter.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'comment': 'Optional comments.', + 'id': '4', + 'ip-addr-block': 'enable', + 'name': 'default_name_6', + 'one-arm-ips-urlfilter': 'enable' + } + + set_method_mock.assert_called_with('webfilter', 'urlfilter', 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_webfilter_urlfilter_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', + 'webfilter_urlfilter': { + 'random_attribute_not_valid': 'tag', + 'comment': 'Optional comments.', + 'id': '4', + 'ip_addr_block': 'enable', + 'name': 'default_name_6', + 'one_arm_ips_urlfilter': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_webfilter_urlfilter.fortios_webfilter(input_data, fos_instance) + + expected_data = { + 'comment': 'Optional comments.', + 'id': '4', + 'ip-addr-block': 'enable', + 'name': 'default_name_6', + 'one-arm-ips-urlfilter': 'enable' + } + + set_method_mock.assert_called_with('webfilter', 'urlfilter', 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_wireless_controller_global.py b/test/units/modules/network/fortios/test_fortios_wireless_controller_global.py new file mode 100644 index 00000000000..50cc5898235 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_wireless_controller_global.py @@ -0,0 +1,279 @@ +# 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_wireless_controller_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_wireless_controller_global.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_wireless_controller_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', + 'wireless_controller_global': { + 'ap_log_server': 'enable', + 'ap_log_server_ip': 'test_value_4', + 'ap_log_server_port': '5', + 'control_message_offload': 'ebp-frame', + 'data_ethernet_II': 'enable', + 'discovery_mc_addr': 'test_value_8', + 'fiapp_eth_type': '9', + 'image_download': 'enable', + 'ipsec_base_ip': 'test_value_11', + 'link_aggregation': 'enable', + 'location': 'test_value_13', + 'max_clients': '14', + 'max_retransmit': '15', + 'mesh_eth_type': '16', + 'name': 'default_name_17', + 'rogue_scan_mac_adjacency': '18', + 'wtp_share': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_global.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'ap-log-server': 'enable', + 'ap-log-server-ip': 'test_value_4', + 'ap-log-server-port': '5', + 'control-message-offload': 'ebp-frame', + 'data-ethernet-II': 'enable', + 'discovery-mc-addr': 'test_value_8', + 'fiapp-eth-type': '9', + 'image-download': 'enable', + 'ipsec-base-ip': 'test_value_11', + 'link-aggregation': 'enable', + 'location': 'test_value_13', + 'max-clients': '14', + 'max-retransmit': '15', + 'mesh-eth-type': '16', + 'name': 'default_name_17', + 'rogue-scan-mac-adjacency': '18', + 'wtp-share': 'enable' + } + + set_method_mock.assert_called_with('wireless-controller', '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_wireless_controller_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', + 'wireless_controller_global': { + 'ap_log_server': 'enable', + 'ap_log_server_ip': 'test_value_4', + 'ap_log_server_port': '5', + 'control_message_offload': 'ebp-frame', + 'data_ethernet_II': 'enable', + 'discovery_mc_addr': 'test_value_8', + 'fiapp_eth_type': '9', + 'image_download': 'enable', + 'ipsec_base_ip': 'test_value_11', + 'link_aggregation': 'enable', + 'location': 'test_value_13', + 'max_clients': '14', + 'max_retransmit': '15', + 'mesh_eth_type': '16', + 'name': 'default_name_17', + 'rogue_scan_mac_adjacency': '18', + 'wtp_share': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_global.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'ap-log-server': 'enable', + 'ap-log-server-ip': 'test_value_4', + 'ap-log-server-port': '5', + 'control-message-offload': 'ebp-frame', + 'data-ethernet-II': 'enable', + 'discovery-mc-addr': 'test_value_8', + 'fiapp-eth-type': '9', + 'image-download': 'enable', + 'ipsec-base-ip': 'test_value_11', + 'link-aggregation': 'enable', + 'location': 'test_value_13', + 'max-clients': '14', + 'max-retransmit': '15', + 'mesh-eth-type': '16', + 'name': 'default_name_17', + 'rogue-scan-mac-adjacency': '18', + 'wtp-share': 'enable' + } + + set_method_mock.assert_called_with('wireless-controller', '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_wireless_controller_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', + 'wireless_controller_global': { + 'ap_log_server': 'enable', + 'ap_log_server_ip': 'test_value_4', + 'ap_log_server_port': '5', + 'control_message_offload': 'ebp-frame', + 'data_ethernet_II': 'enable', + 'discovery_mc_addr': 'test_value_8', + 'fiapp_eth_type': '9', + 'image_download': 'enable', + 'ipsec_base_ip': 'test_value_11', + 'link_aggregation': 'enable', + 'location': 'test_value_13', + 'max_clients': '14', + 'max_retransmit': '15', + 'mesh_eth_type': '16', + 'name': 'default_name_17', + 'rogue_scan_mac_adjacency': '18', + 'wtp_share': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_global.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'ap-log-server': 'enable', + 'ap-log-server-ip': 'test_value_4', + 'ap-log-server-port': '5', + 'control-message-offload': 'ebp-frame', + 'data-ethernet-II': 'enable', + 'discovery-mc-addr': 'test_value_8', + 'fiapp-eth-type': '9', + 'image-download': 'enable', + 'ipsec-base-ip': 'test_value_11', + 'link-aggregation': 'enable', + 'location': 'test_value_13', + 'max-clients': '14', + 'max-retransmit': '15', + 'mesh-eth-type': '16', + 'name': 'default_name_17', + 'rogue-scan-mac-adjacency': '18', + 'wtp-share': 'enable' + } + + set_method_mock.assert_called_with('wireless-controller', '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_wireless_controller_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', + 'wireless_controller_global': { + 'random_attribute_not_valid': 'tag', + 'ap_log_server': 'enable', + 'ap_log_server_ip': 'test_value_4', + 'ap_log_server_port': '5', + 'control_message_offload': 'ebp-frame', + 'data_ethernet_II': 'enable', + 'discovery_mc_addr': 'test_value_8', + 'fiapp_eth_type': '9', + 'image_download': 'enable', + 'ipsec_base_ip': 'test_value_11', + 'link_aggregation': 'enable', + 'location': 'test_value_13', + 'max_clients': '14', + 'max_retransmit': '15', + 'mesh_eth_type': '16', + 'name': 'default_name_17', + 'rogue_scan_mac_adjacency': '18', + 'wtp_share': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_global.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'ap-log-server': 'enable', + 'ap-log-server-ip': 'test_value_4', + 'ap-log-server-port': '5', + 'control-message-offload': 'ebp-frame', + 'data-ethernet-II': 'enable', + 'discovery-mc-addr': 'test_value_8', + 'fiapp-eth-type': '9', + 'image-download': 'enable', + 'ipsec-base-ip': 'test_value_11', + 'link-aggregation': 'enable', + 'location': 'test_value_13', + 'max-clients': '14', + 'max-retransmit': '15', + 'mesh-eth-type': '16', + 'name': 'default_name_17', + 'rogue-scan-mac-adjacency': '18', + 'wtp-share': 'enable' + } + + set_method_mock.assert_called_with('wireless-controller', '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_wireless_controller_setting.py b/test/units/modules/network/fortios/test_fortios_wireless_controller_setting.py new file mode 100644 index 00000000000..5be01593a26 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_wireless_controller_setting.py @@ -0,0 +1,175 @@ +# 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_wireless_controller_setting +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_wireless_controller_setting.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_wireless_controller_setting_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', + 'wireless_controller_setting': { + 'account_id': 'test_value_3', + 'country': 'NA', + 'duplicate_ssid': 'enable', + 'fapc_compatibility': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_setting.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'account-id': 'test_value_3', + 'country': 'NA', + 'duplicate-ssid': 'enable', + 'fapc-compatibility': 'enable' + } + + set_method_mock.assert_called_with('wireless-controller', 'setting', 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_wireless_controller_setting_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', + 'wireless_controller_setting': { + 'account_id': 'test_value_3', + 'country': 'NA', + 'duplicate_ssid': 'enable', + 'fapc_compatibility': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_setting.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'account-id': 'test_value_3', + 'country': 'NA', + 'duplicate-ssid': 'enable', + 'fapc-compatibility': 'enable' + } + + set_method_mock.assert_called_with('wireless-controller', 'setting', 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_wireless_controller_setting_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', + 'wireless_controller_setting': { + 'account_id': 'test_value_3', + 'country': 'NA', + 'duplicate_ssid': 'enable', + 'fapc_compatibility': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_setting.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'account-id': 'test_value_3', + 'country': 'NA', + 'duplicate-ssid': 'enable', + 'fapc-compatibility': 'enable' + } + + set_method_mock.assert_called_with('wireless-controller', 'setting', 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_wireless_controller_setting_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', + 'wireless_controller_setting': { + 'random_attribute_not_valid': 'tag', + 'account_id': 'test_value_3', + 'country': 'NA', + 'duplicate_ssid': 'enable', + 'fapc_compatibility': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_setting.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'account-id': 'test_value_3', + 'country': 'NA', + 'duplicate-ssid': 'enable', + 'fapc-compatibility': 'enable' + } + + set_method_mock.assert_called_with('wireless-controller', 'setting', 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_wireless_controller_utm_profile.py b/test/units/modules/network/fortios/test_fortios_wireless_controller_utm_profile.py new file mode 100644 index 00000000000..6af88cad6a9 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_wireless_controller_utm_profile.py @@ -0,0 +1,269 @@ +# 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_wireless_controller_utm_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_wireless_controller_utm_profile.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_wireless_controller_utm_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', + 'wireless_controller_utm_profile': { + 'antivirus_profile': 'test_value_3', + 'application_list': 'test_value_4', + 'comment': 'Comment.', + 'ips_sensor': 'test_value_6', + 'name': 'default_name_7', + 'scan_botnet_connections': 'disable', + 'utm_log': 'enable', + 'webfilter_profile': 'test_value_10' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_utm_profile.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'antivirus-profile': 'test_value_3', + 'application-list': 'test_value_4', + 'comment': 'Comment.', + 'ips-sensor': 'test_value_6', + 'name': 'default_name_7', + 'scan-botnet-connections': 'disable', + 'utm-log': 'enable', + 'webfilter-profile': 'test_value_10' + } + + set_method_mock.assert_called_with('wireless-controller', 'utm-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_wireless_controller_utm_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', + 'wireless_controller_utm_profile': { + 'antivirus_profile': 'test_value_3', + 'application_list': 'test_value_4', + 'comment': 'Comment.', + 'ips_sensor': 'test_value_6', + 'name': 'default_name_7', + 'scan_botnet_connections': 'disable', + 'utm_log': 'enable', + 'webfilter_profile': 'test_value_10' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_utm_profile.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'antivirus-profile': 'test_value_3', + 'application-list': 'test_value_4', + 'comment': 'Comment.', + 'ips-sensor': 'test_value_6', + 'name': 'default_name_7', + 'scan-botnet-connections': 'disable', + 'utm-log': 'enable', + 'webfilter-profile': 'test_value_10' + } + + set_method_mock.assert_called_with('wireless-controller', 'utm-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_wireless_controller_utm_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', + 'wireless_controller_utm_profile': { + 'antivirus_profile': 'test_value_3', + 'application_list': 'test_value_4', + 'comment': 'Comment.', + 'ips_sensor': 'test_value_6', + 'name': 'default_name_7', + 'scan_botnet_connections': 'disable', + 'utm_log': 'enable', + 'webfilter_profile': 'test_value_10' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_utm_profile.fortios_wireless_controller(input_data, fos_instance) + + delete_method_mock.assert_called_with('wireless-controller', 'utm-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_wireless_controller_utm_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', + 'wireless_controller_utm_profile': { + 'antivirus_profile': 'test_value_3', + 'application_list': 'test_value_4', + 'comment': 'Comment.', + 'ips_sensor': 'test_value_6', + 'name': 'default_name_7', + 'scan_botnet_connections': 'disable', + 'utm_log': 'enable', + 'webfilter_profile': 'test_value_10' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_utm_profile.fortios_wireless_controller(input_data, fos_instance) + + delete_method_mock.assert_called_with('wireless-controller', 'utm-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_wireless_controller_utm_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', + 'wireless_controller_utm_profile': { + 'antivirus_profile': 'test_value_3', + 'application_list': 'test_value_4', + 'comment': 'Comment.', + 'ips_sensor': 'test_value_6', + 'name': 'default_name_7', + 'scan_botnet_connections': 'disable', + 'utm_log': 'enable', + 'webfilter_profile': 'test_value_10' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_utm_profile.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'antivirus-profile': 'test_value_3', + 'application-list': 'test_value_4', + 'comment': 'Comment.', + 'ips-sensor': 'test_value_6', + 'name': 'default_name_7', + 'scan-botnet-connections': 'disable', + 'utm-log': 'enable', + 'webfilter-profile': 'test_value_10' + } + + set_method_mock.assert_called_with('wireless-controller', 'utm-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_wireless_controller_utm_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', + 'wireless_controller_utm_profile': { + 'random_attribute_not_valid': 'tag', + 'antivirus_profile': 'test_value_3', + 'application_list': 'test_value_4', + 'comment': 'Comment.', + 'ips_sensor': 'test_value_6', + 'name': 'default_name_7', + 'scan_botnet_connections': 'disable', + 'utm_log': 'enable', + 'webfilter_profile': 'test_value_10' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_utm_profile.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'antivirus-profile': 'test_value_3', + 'application-list': 'test_value_4', + 'comment': 'Comment.', + 'ips-sensor': 'test_value_6', + 'name': 'default_name_7', + 'scan-botnet-connections': 'disable', + 'utm-log': 'enable', + 'webfilter-profile': 'test_value_10' + } + + set_method_mock.assert_called_with('wireless-controller', 'utm-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_wireless_controller_vap.py b/test/units/modules/network/fortios/test_fortios_wireless_controller_vap.py new file mode 100644 index 00000000000..fcfc7ce0942 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_wireless_controller_vap.py @@ -0,0 +1,1109 @@ +# 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_wireless_controller_vap +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_wireless_controller_vap.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_wireless_controller_vap_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', + 'wireless_controller_vap': { + 'acct_interim_interval': '3', + 'alias': 'test_value_4', + 'auth': 'psk', + 'broadcast_ssid': 'enable', + 'broadcast_suppression': 'dhcp-up', + 'captive_portal_ac_name': 'test_value_8', + 'captive_portal_macauth_radius_secret': 'test_value_9', + 'captive_portal_macauth_radius_server': 'test_value_10', + 'captive_portal_radius_secret': 'test_value_11', + 'captive_portal_radius_server': 'test_value_12', + 'captive_portal_session_timeout_interval': '13', + 'dhcp_lease_time': '14', + 'dhcp_option82_circuit_id_insertion': 'style-1', + 'dhcp_option82_insertion': 'enable', + 'dhcp_option82_remote_id_insertion': 'style-1', + 'dynamic_vlan': 'enable', + 'eap_reauth': 'enable', + 'eap_reauth_intv': '20', + 'eapol_key_retries': 'disable', + 'encrypt': 'TKIP', + 'external_fast_roaming': 'enable', + 'external_logout': 'test_value_24', + 'external_web': 'test_value_25', + 'fast_bss_transition': 'disable', + 'fast_roaming': 'enable', + 'ft_mobility_domain': '28', + 'ft_over_ds': 'disable', + 'ft_r0_key_lifetime': '30', + 'gtk_rekey': 'enable', + 'gtk_rekey_intv': '32', + 'hotspot20_profile': 'test_value_33', + 'intra_vap_privacy': 'enable', + 'ip': 'test_value_35', + 'key': 'test_value_36', + 'keyindex': '37', + 'ldpc': 'disable', + 'local_authentication': 'enable', + 'local_bridging': 'enable', + 'local_lan': 'allow', + 'local_standalone': 'enable', + 'local_standalone_nat': 'enable', + 'mac_auth_bypass': 'enable', + 'mac_filter': 'enable', + 'mac_filter_policy_other': 'allow', + 'max_clients': '47', + 'max_clients_ap': '48', + 'me_disable_thresh': '49', + 'mesh_backhaul': 'enable', + 'mpsk': 'enable', + 'mpsk_concurrent_clients': '52', + 'multicast_enhance': 'enable', + 'multicast_rate': '0', + 'name': 'default_name_55', + 'okc': 'disable', + 'passphrase': 'test_value_57', + 'pmf': 'disable', + 'pmf_assoc_comeback_timeout': '59', + 'pmf_sa_query_retry_timeout': '60', + 'portal_message_override_group': 'test_value_61', + 'portal_type': 'auth', + 'probe_resp_suppression': 'enable', + 'probe_resp_threshold': 'test_value_64', + 'ptk_rekey': 'enable', + 'ptk_rekey_intv': '66', + 'qos_profile': 'test_value_67', + 'quarantine': 'enable', + 'radio_2g_threshold': 'test_value_69', + 'radio_5g_threshold': 'test_value_70', + 'radio_sensitivity': 'enable', + 'radius_mac_auth': 'enable', + 'radius_mac_auth_server': 'test_value_73', + 'radius_server': 'test_value_74', + 'rates_11a': '1', + 'rates_11ac_ss12': 'mcs0/1', + 'rates_11ac_ss34': 'mcs0/3', + 'rates_11bg': '1', + 'rates_11n_ss12': 'mcs0/1', + 'rates_11n_ss34': 'mcs16/3', + 'schedule': 'test_value_81', + 'security': 'open', + 'security_exempt_list': 'test_value_83', + 'security_obsolete_option': 'enable', + 'security_redirect_url': 'test_value_85', + 'split_tunneling': 'enable', + 'ssid': 'test_value_87', + 'tkip_counter_measure': 'enable', + 'utm_profile': 'test_value_89', + 'vdom': 'test_value_90', + 'vlan_auto': 'enable', + 'vlan_pooling': 'wtp-group', + 'vlanid': '93', + 'voice_enterprise': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_vap.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'acct-interim-interval': '3', + 'alias': 'test_value_4', + 'auth': 'psk', + 'broadcast-ssid': 'enable', + 'broadcast-suppression': 'dhcp-up', + 'captive-portal-ac-name': 'test_value_8', + 'captive-portal-macauth-radius-secret': 'test_value_9', + 'captive-portal-macauth-radius-server': 'test_value_10', + 'captive-portal-radius-secret': 'test_value_11', + 'captive-portal-radius-server': 'test_value_12', + 'captive-portal-session-timeout-interval': '13', + 'dhcp-lease-time': '14', + 'dhcp-option82-circuit-id-insertion': 'style-1', + 'dhcp-option82-insertion': 'enable', + 'dhcp-option82-remote-id-insertion': 'style-1', + 'dynamic-vlan': 'enable', + 'eap-reauth': 'enable', + 'eap-reauth-intv': '20', + 'eapol-key-retries': 'disable', + 'encrypt': 'TKIP', + 'external-fast-roaming': 'enable', + 'external-logout': 'test_value_24', + 'external-web': 'test_value_25', + 'fast-bss-transition': 'disable', + 'fast-roaming': 'enable', + 'ft-mobility-domain': '28', + 'ft-over-ds': 'disable', + 'ft-r0-key-lifetime': '30', + 'gtk-rekey': 'enable', + 'gtk-rekey-intv': '32', + 'hotspot20-profile': 'test_value_33', + 'intra-vap-privacy': 'enable', + 'ip': 'test_value_35', + 'key': 'test_value_36', + 'keyindex': '37', + 'ldpc': 'disable', + 'local-authentication': 'enable', + 'local-bridging': 'enable', + 'local-lan': 'allow', + 'local-standalone': 'enable', + 'local-standalone-nat': 'enable', + 'mac-auth-bypass': 'enable', + 'mac-filter': 'enable', + 'mac-filter-policy-other': 'allow', + 'max-clients': '47', + 'max-clients-ap': '48', + 'me-disable-thresh': '49', + 'mesh-backhaul': 'enable', + 'mpsk': 'enable', + 'mpsk-concurrent-clients': '52', + 'multicast-enhance': 'enable', + 'multicast-rate': '0', + 'name': 'default_name_55', + 'okc': 'disable', + 'passphrase': 'test_value_57', + 'pmf': 'disable', + 'pmf-assoc-comeback-timeout': '59', + 'pmf-sa-query-retry-timeout': '60', + 'portal-message-override-group': 'test_value_61', + 'portal-type': 'auth', + 'probe-resp-suppression': 'enable', + 'probe-resp-threshold': 'test_value_64', + 'ptk-rekey': 'enable', + 'ptk-rekey-intv': '66', + 'qos-profile': 'test_value_67', + 'quarantine': 'enable', + 'radio-2g-threshold': 'test_value_69', + 'radio-5g-threshold': 'test_value_70', + 'radio-sensitivity': 'enable', + 'radius-mac-auth': 'enable', + 'radius-mac-auth-server': 'test_value_73', + 'radius-server': 'test_value_74', + 'rates-11a': '1', + 'rates-11ac-ss12': 'mcs0/1', + 'rates-11ac-ss34': 'mcs0/3', + 'rates-11bg': '1', + 'rates-11n-ss12': 'mcs0/1', + 'rates-11n-ss34': 'mcs16/3', + 'schedule': 'test_value_81', + 'security': 'open', + 'security-exempt-list': 'test_value_83', + 'security-obsolete-option': 'enable', + 'security-redirect-url': 'test_value_85', + 'split-tunneling': 'enable', + 'ssid': 'test_value_87', + 'tkip-counter-measure': 'enable', + 'utm-profile': 'test_value_89', + 'vdom': 'test_value_90', + 'vlan-auto': 'enable', + 'vlan-pooling': 'wtp-group', + 'vlanid': '93', + 'voice-enterprise': 'disable' + } + + set_method_mock.assert_called_with('wireless-controller', 'vap', 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_wireless_controller_vap_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', + 'wireless_controller_vap': { + 'acct_interim_interval': '3', + 'alias': 'test_value_4', + 'auth': 'psk', + 'broadcast_ssid': 'enable', + 'broadcast_suppression': 'dhcp-up', + 'captive_portal_ac_name': 'test_value_8', + 'captive_portal_macauth_radius_secret': 'test_value_9', + 'captive_portal_macauth_radius_server': 'test_value_10', + 'captive_portal_radius_secret': 'test_value_11', + 'captive_portal_radius_server': 'test_value_12', + 'captive_portal_session_timeout_interval': '13', + 'dhcp_lease_time': '14', + 'dhcp_option82_circuit_id_insertion': 'style-1', + 'dhcp_option82_insertion': 'enable', + 'dhcp_option82_remote_id_insertion': 'style-1', + 'dynamic_vlan': 'enable', + 'eap_reauth': 'enable', + 'eap_reauth_intv': '20', + 'eapol_key_retries': 'disable', + 'encrypt': 'TKIP', + 'external_fast_roaming': 'enable', + 'external_logout': 'test_value_24', + 'external_web': 'test_value_25', + 'fast_bss_transition': 'disable', + 'fast_roaming': 'enable', + 'ft_mobility_domain': '28', + 'ft_over_ds': 'disable', + 'ft_r0_key_lifetime': '30', + 'gtk_rekey': 'enable', + 'gtk_rekey_intv': '32', + 'hotspot20_profile': 'test_value_33', + 'intra_vap_privacy': 'enable', + 'ip': 'test_value_35', + 'key': 'test_value_36', + 'keyindex': '37', + 'ldpc': 'disable', + 'local_authentication': 'enable', + 'local_bridging': 'enable', + 'local_lan': 'allow', + 'local_standalone': 'enable', + 'local_standalone_nat': 'enable', + 'mac_auth_bypass': 'enable', + 'mac_filter': 'enable', + 'mac_filter_policy_other': 'allow', + 'max_clients': '47', + 'max_clients_ap': '48', + 'me_disable_thresh': '49', + 'mesh_backhaul': 'enable', + 'mpsk': 'enable', + 'mpsk_concurrent_clients': '52', + 'multicast_enhance': 'enable', + 'multicast_rate': '0', + 'name': 'default_name_55', + 'okc': 'disable', + 'passphrase': 'test_value_57', + 'pmf': 'disable', + 'pmf_assoc_comeback_timeout': '59', + 'pmf_sa_query_retry_timeout': '60', + 'portal_message_override_group': 'test_value_61', + 'portal_type': 'auth', + 'probe_resp_suppression': 'enable', + 'probe_resp_threshold': 'test_value_64', + 'ptk_rekey': 'enable', + 'ptk_rekey_intv': '66', + 'qos_profile': 'test_value_67', + 'quarantine': 'enable', + 'radio_2g_threshold': 'test_value_69', + 'radio_5g_threshold': 'test_value_70', + 'radio_sensitivity': 'enable', + 'radius_mac_auth': 'enable', + 'radius_mac_auth_server': 'test_value_73', + 'radius_server': 'test_value_74', + 'rates_11a': '1', + 'rates_11ac_ss12': 'mcs0/1', + 'rates_11ac_ss34': 'mcs0/3', + 'rates_11bg': '1', + 'rates_11n_ss12': 'mcs0/1', + 'rates_11n_ss34': 'mcs16/3', + 'schedule': 'test_value_81', + 'security': 'open', + 'security_exempt_list': 'test_value_83', + 'security_obsolete_option': 'enable', + 'security_redirect_url': 'test_value_85', + 'split_tunneling': 'enable', + 'ssid': 'test_value_87', + 'tkip_counter_measure': 'enable', + 'utm_profile': 'test_value_89', + 'vdom': 'test_value_90', + 'vlan_auto': 'enable', + 'vlan_pooling': 'wtp-group', + 'vlanid': '93', + 'voice_enterprise': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_vap.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'acct-interim-interval': '3', + 'alias': 'test_value_4', + 'auth': 'psk', + 'broadcast-ssid': 'enable', + 'broadcast-suppression': 'dhcp-up', + 'captive-portal-ac-name': 'test_value_8', + 'captive-portal-macauth-radius-secret': 'test_value_9', + 'captive-portal-macauth-radius-server': 'test_value_10', + 'captive-portal-radius-secret': 'test_value_11', + 'captive-portal-radius-server': 'test_value_12', + 'captive-portal-session-timeout-interval': '13', + 'dhcp-lease-time': '14', + 'dhcp-option82-circuit-id-insertion': 'style-1', + 'dhcp-option82-insertion': 'enable', + 'dhcp-option82-remote-id-insertion': 'style-1', + 'dynamic-vlan': 'enable', + 'eap-reauth': 'enable', + 'eap-reauth-intv': '20', + 'eapol-key-retries': 'disable', + 'encrypt': 'TKIP', + 'external-fast-roaming': 'enable', + 'external-logout': 'test_value_24', + 'external-web': 'test_value_25', + 'fast-bss-transition': 'disable', + 'fast-roaming': 'enable', + 'ft-mobility-domain': '28', + 'ft-over-ds': 'disable', + 'ft-r0-key-lifetime': '30', + 'gtk-rekey': 'enable', + 'gtk-rekey-intv': '32', + 'hotspot20-profile': 'test_value_33', + 'intra-vap-privacy': 'enable', + 'ip': 'test_value_35', + 'key': 'test_value_36', + 'keyindex': '37', + 'ldpc': 'disable', + 'local-authentication': 'enable', + 'local-bridging': 'enable', + 'local-lan': 'allow', + 'local-standalone': 'enable', + 'local-standalone-nat': 'enable', + 'mac-auth-bypass': 'enable', + 'mac-filter': 'enable', + 'mac-filter-policy-other': 'allow', + 'max-clients': '47', + 'max-clients-ap': '48', + 'me-disable-thresh': '49', + 'mesh-backhaul': 'enable', + 'mpsk': 'enable', + 'mpsk-concurrent-clients': '52', + 'multicast-enhance': 'enable', + 'multicast-rate': '0', + 'name': 'default_name_55', + 'okc': 'disable', + 'passphrase': 'test_value_57', + 'pmf': 'disable', + 'pmf-assoc-comeback-timeout': '59', + 'pmf-sa-query-retry-timeout': '60', + 'portal-message-override-group': 'test_value_61', + 'portal-type': 'auth', + 'probe-resp-suppression': 'enable', + 'probe-resp-threshold': 'test_value_64', + 'ptk-rekey': 'enable', + 'ptk-rekey-intv': '66', + 'qos-profile': 'test_value_67', + 'quarantine': 'enable', + 'radio-2g-threshold': 'test_value_69', + 'radio-5g-threshold': 'test_value_70', + 'radio-sensitivity': 'enable', + 'radius-mac-auth': 'enable', + 'radius-mac-auth-server': 'test_value_73', + 'radius-server': 'test_value_74', + 'rates-11a': '1', + 'rates-11ac-ss12': 'mcs0/1', + 'rates-11ac-ss34': 'mcs0/3', + 'rates-11bg': '1', + 'rates-11n-ss12': 'mcs0/1', + 'rates-11n-ss34': 'mcs16/3', + 'schedule': 'test_value_81', + 'security': 'open', + 'security-exempt-list': 'test_value_83', + 'security-obsolete-option': 'enable', + 'security-redirect-url': 'test_value_85', + 'split-tunneling': 'enable', + 'ssid': 'test_value_87', + 'tkip-counter-measure': 'enable', + 'utm-profile': 'test_value_89', + 'vdom': 'test_value_90', + 'vlan-auto': 'enable', + 'vlan-pooling': 'wtp-group', + 'vlanid': '93', + 'voice-enterprise': 'disable' + } + + set_method_mock.assert_called_with('wireless-controller', 'vap', 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_wireless_controller_vap_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', + 'wireless_controller_vap': { + 'acct_interim_interval': '3', + 'alias': 'test_value_4', + 'auth': 'psk', + 'broadcast_ssid': 'enable', + 'broadcast_suppression': 'dhcp-up', + 'captive_portal_ac_name': 'test_value_8', + 'captive_portal_macauth_radius_secret': 'test_value_9', + 'captive_portal_macauth_radius_server': 'test_value_10', + 'captive_portal_radius_secret': 'test_value_11', + 'captive_portal_radius_server': 'test_value_12', + 'captive_portal_session_timeout_interval': '13', + 'dhcp_lease_time': '14', + 'dhcp_option82_circuit_id_insertion': 'style-1', + 'dhcp_option82_insertion': 'enable', + 'dhcp_option82_remote_id_insertion': 'style-1', + 'dynamic_vlan': 'enable', + 'eap_reauth': 'enable', + 'eap_reauth_intv': '20', + 'eapol_key_retries': 'disable', + 'encrypt': 'TKIP', + 'external_fast_roaming': 'enable', + 'external_logout': 'test_value_24', + 'external_web': 'test_value_25', + 'fast_bss_transition': 'disable', + 'fast_roaming': 'enable', + 'ft_mobility_domain': '28', + 'ft_over_ds': 'disable', + 'ft_r0_key_lifetime': '30', + 'gtk_rekey': 'enable', + 'gtk_rekey_intv': '32', + 'hotspot20_profile': 'test_value_33', + 'intra_vap_privacy': 'enable', + 'ip': 'test_value_35', + 'key': 'test_value_36', + 'keyindex': '37', + 'ldpc': 'disable', + 'local_authentication': 'enable', + 'local_bridging': 'enable', + 'local_lan': 'allow', + 'local_standalone': 'enable', + 'local_standalone_nat': 'enable', + 'mac_auth_bypass': 'enable', + 'mac_filter': 'enable', + 'mac_filter_policy_other': 'allow', + 'max_clients': '47', + 'max_clients_ap': '48', + 'me_disable_thresh': '49', + 'mesh_backhaul': 'enable', + 'mpsk': 'enable', + 'mpsk_concurrent_clients': '52', + 'multicast_enhance': 'enable', + 'multicast_rate': '0', + 'name': 'default_name_55', + 'okc': 'disable', + 'passphrase': 'test_value_57', + 'pmf': 'disable', + 'pmf_assoc_comeback_timeout': '59', + 'pmf_sa_query_retry_timeout': '60', + 'portal_message_override_group': 'test_value_61', + 'portal_type': 'auth', + 'probe_resp_suppression': 'enable', + 'probe_resp_threshold': 'test_value_64', + 'ptk_rekey': 'enable', + 'ptk_rekey_intv': '66', + 'qos_profile': 'test_value_67', + 'quarantine': 'enable', + 'radio_2g_threshold': 'test_value_69', + 'radio_5g_threshold': 'test_value_70', + 'radio_sensitivity': 'enable', + 'radius_mac_auth': 'enable', + 'radius_mac_auth_server': 'test_value_73', + 'radius_server': 'test_value_74', + 'rates_11a': '1', + 'rates_11ac_ss12': 'mcs0/1', + 'rates_11ac_ss34': 'mcs0/3', + 'rates_11bg': '1', + 'rates_11n_ss12': 'mcs0/1', + 'rates_11n_ss34': 'mcs16/3', + 'schedule': 'test_value_81', + 'security': 'open', + 'security_exempt_list': 'test_value_83', + 'security_obsolete_option': 'enable', + 'security_redirect_url': 'test_value_85', + 'split_tunneling': 'enable', + 'ssid': 'test_value_87', + 'tkip_counter_measure': 'enable', + 'utm_profile': 'test_value_89', + 'vdom': 'test_value_90', + 'vlan_auto': 'enable', + 'vlan_pooling': 'wtp-group', + 'vlanid': '93', + 'voice_enterprise': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_vap.fortios_wireless_controller(input_data, fos_instance) + + delete_method_mock.assert_called_with('wireless-controller', 'vap', 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_wireless_controller_vap_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', + 'wireless_controller_vap': { + 'acct_interim_interval': '3', + 'alias': 'test_value_4', + 'auth': 'psk', + 'broadcast_ssid': 'enable', + 'broadcast_suppression': 'dhcp-up', + 'captive_portal_ac_name': 'test_value_8', + 'captive_portal_macauth_radius_secret': 'test_value_9', + 'captive_portal_macauth_radius_server': 'test_value_10', + 'captive_portal_radius_secret': 'test_value_11', + 'captive_portal_radius_server': 'test_value_12', + 'captive_portal_session_timeout_interval': '13', + 'dhcp_lease_time': '14', + 'dhcp_option82_circuit_id_insertion': 'style-1', + 'dhcp_option82_insertion': 'enable', + 'dhcp_option82_remote_id_insertion': 'style-1', + 'dynamic_vlan': 'enable', + 'eap_reauth': 'enable', + 'eap_reauth_intv': '20', + 'eapol_key_retries': 'disable', + 'encrypt': 'TKIP', + 'external_fast_roaming': 'enable', + 'external_logout': 'test_value_24', + 'external_web': 'test_value_25', + 'fast_bss_transition': 'disable', + 'fast_roaming': 'enable', + 'ft_mobility_domain': '28', + 'ft_over_ds': 'disable', + 'ft_r0_key_lifetime': '30', + 'gtk_rekey': 'enable', + 'gtk_rekey_intv': '32', + 'hotspot20_profile': 'test_value_33', + 'intra_vap_privacy': 'enable', + 'ip': 'test_value_35', + 'key': 'test_value_36', + 'keyindex': '37', + 'ldpc': 'disable', + 'local_authentication': 'enable', + 'local_bridging': 'enable', + 'local_lan': 'allow', + 'local_standalone': 'enable', + 'local_standalone_nat': 'enable', + 'mac_auth_bypass': 'enable', + 'mac_filter': 'enable', + 'mac_filter_policy_other': 'allow', + 'max_clients': '47', + 'max_clients_ap': '48', + 'me_disable_thresh': '49', + 'mesh_backhaul': 'enable', + 'mpsk': 'enable', + 'mpsk_concurrent_clients': '52', + 'multicast_enhance': 'enable', + 'multicast_rate': '0', + 'name': 'default_name_55', + 'okc': 'disable', + 'passphrase': 'test_value_57', + 'pmf': 'disable', + 'pmf_assoc_comeback_timeout': '59', + 'pmf_sa_query_retry_timeout': '60', + 'portal_message_override_group': 'test_value_61', + 'portal_type': 'auth', + 'probe_resp_suppression': 'enable', + 'probe_resp_threshold': 'test_value_64', + 'ptk_rekey': 'enable', + 'ptk_rekey_intv': '66', + 'qos_profile': 'test_value_67', + 'quarantine': 'enable', + 'radio_2g_threshold': 'test_value_69', + 'radio_5g_threshold': 'test_value_70', + 'radio_sensitivity': 'enable', + 'radius_mac_auth': 'enable', + 'radius_mac_auth_server': 'test_value_73', + 'radius_server': 'test_value_74', + 'rates_11a': '1', + 'rates_11ac_ss12': 'mcs0/1', + 'rates_11ac_ss34': 'mcs0/3', + 'rates_11bg': '1', + 'rates_11n_ss12': 'mcs0/1', + 'rates_11n_ss34': 'mcs16/3', + 'schedule': 'test_value_81', + 'security': 'open', + 'security_exempt_list': 'test_value_83', + 'security_obsolete_option': 'enable', + 'security_redirect_url': 'test_value_85', + 'split_tunneling': 'enable', + 'ssid': 'test_value_87', + 'tkip_counter_measure': 'enable', + 'utm_profile': 'test_value_89', + 'vdom': 'test_value_90', + 'vlan_auto': 'enable', + 'vlan_pooling': 'wtp-group', + 'vlanid': '93', + 'voice_enterprise': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_vap.fortios_wireless_controller(input_data, fos_instance) + + delete_method_mock.assert_called_with('wireless-controller', 'vap', 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_wireless_controller_vap_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', + 'wireless_controller_vap': { + 'acct_interim_interval': '3', + 'alias': 'test_value_4', + 'auth': 'psk', + 'broadcast_ssid': 'enable', + 'broadcast_suppression': 'dhcp-up', + 'captive_portal_ac_name': 'test_value_8', + 'captive_portal_macauth_radius_secret': 'test_value_9', + 'captive_portal_macauth_radius_server': 'test_value_10', + 'captive_portal_radius_secret': 'test_value_11', + 'captive_portal_radius_server': 'test_value_12', + 'captive_portal_session_timeout_interval': '13', + 'dhcp_lease_time': '14', + 'dhcp_option82_circuit_id_insertion': 'style-1', + 'dhcp_option82_insertion': 'enable', + 'dhcp_option82_remote_id_insertion': 'style-1', + 'dynamic_vlan': 'enable', + 'eap_reauth': 'enable', + 'eap_reauth_intv': '20', + 'eapol_key_retries': 'disable', + 'encrypt': 'TKIP', + 'external_fast_roaming': 'enable', + 'external_logout': 'test_value_24', + 'external_web': 'test_value_25', + 'fast_bss_transition': 'disable', + 'fast_roaming': 'enable', + 'ft_mobility_domain': '28', + 'ft_over_ds': 'disable', + 'ft_r0_key_lifetime': '30', + 'gtk_rekey': 'enable', + 'gtk_rekey_intv': '32', + 'hotspot20_profile': 'test_value_33', + 'intra_vap_privacy': 'enable', + 'ip': 'test_value_35', + 'key': 'test_value_36', + 'keyindex': '37', + 'ldpc': 'disable', + 'local_authentication': 'enable', + 'local_bridging': 'enable', + 'local_lan': 'allow', + 'local_standalone': 'enable', + 'local_standalone_nat': 'enable', + 'mac_auth_bypass': 'enable', + 'mac_filter': 'enable', + 'mac_filter_policy_other': 'allow', + 'max_clients': '47', + 'max_clients_ap': '48', + 'me_disable_thresh': '49', + 'mesh_backhaul': 'enable', + 'mpsk': 'enable', + 'mpsk_concurrent_clients': '52', + 'multicast_enhance': 'enable', + 'multicast_rate': '0', + 'name': 'default_name_55', + 'okc': 'disable', + 'passphrase': 'test_value_57', + 'pmf': 'disable', + 'pmf_assoc_comeback_timeout': '59', + 'pmf_sa_query_retry_timeout': '60', + 'portal_message_override_group': 'test_value_61', + 'portal_type': 'auth', + 'probe_resp_suppression': 'enable', + 'probe_resp_threshold': 'test_value_64', + 'ptk_rekey': 'enable', + 'ptk_rekey_intv': '66', + 'qos_profile': 'test_value_67', + 'quarantine': 'enable', + 'radio_2g_threshold': 'test_value_69', + 'radio_5g_threshold': 'test_value_70', + 'radio_sensitivity': 'enable', + 'radius_mac_auth': 'enable', + 'radius_mac_auth_server': 'test_value_73', + 'radius_server': 'test_value_74', + 'rates_11a': '1', + 'rates_11ac_ss12': 'mcs0/1', + 'rates_11ac_ss34': 'mcs0/3', + 'rates_11bg': '1', + 'rates_11n_ss12': 'mcs0/1', + 'rates_11n_ss34': 'mcs16/3', + 'schedule': 'test_value_81', + 'security': 'open', + 'security_exempt_list': 'test_value_83', + 'security_obsolete_option': 'enable', + 'security_redirect_url': 'test_value_85', + 'split_tunneling': 'enable', + 'ssid': 'test_value_87', + 'tkip_counter_measure': 'enable', + 'utm_profile': 'test_value_89', + 'vdom': 'test_value_90', + 'vlan_auto': 'enable', + 'vlan_pooling': 'wtp-group', + 'vlanid': '93', + 'voice_enterprise': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_vap.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'acct-interim-interval': '3', + 'alias': 'test_value_4', + 'auth': 'psk', + 'broadcast-ssid': 'enable', + 'broadcast-suppression': 'dhcp-up', + 'captive-portal-ac-name': 'test_value_8', + 'captive-portal-macauth-radius-secret': 'test_value_9', + 'captive-portal-macauth-radius-server': 'test_value_10', + 'captive-portal-radius-secret': 'test_value_11', + 'captive-portal-radius-server': 'test_value_12', + 'captive-portal-session-timeout-interval': '13', + 'dhcp-lease-time': '14', + 'dhcp-option82-circuit-id-insertion': 'style-1', + 'dhcp-option82-insertion': 'enable', + 'dhcp-option82-remote-id-insertion': 'style-1', + 'dynamic-vlan': 'enable', + 'eap-reauth': 'enable', + 'eap-reauth-intv': '20', + 'eapol-key-retries': 'disable', + 'encrypt': 'TKIP', + 'external-fast-roaming': 'enable', + 'external-logout': 'test_value_24', + 'external-web': 'test_value_25', + 'fast-bss-transition': 'disable', + 'fast-roaming': 'enable', + 'ft-mobility-domain': '28', + 'ft-over-ds': 'disable', + 'ft-r0-key-lifetime': '30', + 'gtk-rekey': 'enable', + 'gtk-rekey-intv': '32', + 'hotspot20-profile': 'test_value_33', + 'intra-vap-privacy': 'enable', + 'ip': 'test_value_35', + 'key': 'test_value_36', + 'keyindex': '37', + 'ldpc': 'disable', + 'local-authentication': 'enable', + 'local-bridging': 'enable', + 'local-lan': 'allow', + 'local-standalone': 'enable', + 'local-standalone-nat': 'enable', + 'mac-auth-bypass': 'enable', + 'mac-filter': 'enable', + 'mac-filter-policy-other': 'allow', + 'max-clients': '47', + 'max-clients-ap': '48', + 'me-disable-thresh': '49', + 'mesh-backhaul': 'enable', + 'mpsk': 'enable', + 'mpsk-concurrent-clients': '52', + 'multicast-enhance': 'enable', + 'multicast-rate': '0', + 'name': 'default_name_55', + 'okc': 'disable', + 'passphrase': 'test_value_57', + 'pmf': 'disable', + 'pmf-assoc-comeback-timeout': '59', + 'pmf-sa-query-retry-timeout': '60', + 'portal-message-override-group': 'test_value_61', + 'portal-type': 'auth', + 'probe-resp-suppression': 'enable', + 'probe-resp-threshold': 'test_value_64', + 'ptk-rekey': 'enable', + 'ptk-rekey-intv': '66', + 'qos-profile': 'test_value_67', + 'quarantine': 'enable', + 'radio-2g-threshold': 'test_value_69', + 'radio-5g-threshold': 'test_value_70', + 'radio-sensitivity': 'enable', + 'radius-mac-auth': 'enable', + 'radius-mac-auth-server': 'test_value_73', + 'radius-server': 'test_value_74', + 'rates-11a': '1', + 'rates-11ac-ss12': 'mcs0/1', + 'rates-11ac-ss34': 'mcs0/3', + 'rates-11bg': '1', + 'rates-11n-ss12': 'mcs0/1', + 'rates-11n-ss34': 'mcs16/3', + 'schedule': 'test_value_81', + 'security': 'open', + 'security-exempt-list': 'test_value_83', + 'security-obsolete-option': 'enable', + 'security-redirect-url': 'test_value_85', + 'split-tunneling': 'enable', + 'ssid': 'test_value_87', + 'tkip-counter-measure': 'enable', + 'utm-profile': 'test_value_89', + 'vdom': 'test_value_90', + 'vlan-auto': 'enable', + 'vlan-pooling': 'wtp-group', + 'vlanid': '93', + 'voice-enterprise': 'disable' + } + + set_method_mock.assert_called_with('wireless-controller', 'vap', 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_wireless_controller_vap_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', + 'wireless_controller_vap': { + 'random_attribute_not_valid': 'tag', + 'acct_interim_interval': '3', + 'alias': 'test_value_4', + 'auth': 'psk', + 'broadcast_ssid': 'enable', + 'broadcast_suppression': 'dhcp-up', + 'captive_portal_ac_name': 'test_value_8', + 'captive_portal_macauth_radius_secret': 'test_value_9', + 'captive_portal_macauth_radius_server': 'test_value_10', + 'captive_portal_radius_secret': 'test_value_11', + 'captive_portal_radius_server': 'test_value_12', + 'captive_portal_session_timeout_interval': '13', + 'dhcp_lease_time': '14', + 'dhcp_option82_circuit_id_insertion': 'style-1', + 'dhcp_option82_insertion': 'enable', + 'dhcp_option82_remote_id_insertion': 'style-1', + 'dynamic_vlan': 'enable', + 'eap_reauth': 'enable', + 'eap_reauth_intv': '20', + 'eapol_key_retries': 'disable', + 'encrypt': 'TKIP', + 'external_fast_roaming': 'enable', + 'external_logout': 'test_value_24', + 'external_web': 'test_value_25', + 'fast_bss_transition': 'disable', + 'fast_roaming': 'enable', + 'ft_mobility_domain': '28', + 'ft_over_ds': 'disable', + 'ft_r0_key_lifetime': '30', + 'gtk_rekey': 'enable', + 'gtk_rekey_intv': '32', + 'hotspot20_profile': 'test_value_33', + 'intra_vap_privacy': 'enable', + 'ip': 'test_value_35', + 'key': 'test_value_36', + 'keyindex': '37', + 'ldpc': 'disable', + 'local_authentication': 'enable', + 'local_bridging': 'enable', + 'local_lan': 'allow', + 'local_standalone': 'enable', + 'local_standalone_nat': 'enable', + 'mac_auth_bypass': 'enable', + 'mac_filter': 'enable', + 'mac_filter_policy_other': 'allow', + 'max_clients': '47', + 'max_clients_ap': '48', + 'me_disable_thresh': '49', + 'mesh_backhaul': 'enable', + 'mpsk': 'enable', + 'mpsk_concurrent_clients': '52', + 'multicast_enhance': 'enable', + 'multicast_rate': '0', + 'name': 'default_name_55', + 'okc': 'disable', + 'passphrase': 'test_value_57', + 'pmf': 'disable', + 'pmf_assoc_comeback_timeout': '59', + 'pmf_sa_query_retry_timeout': '60', + 'portal_message_override_group': 'test_value_61', + 'portal_type': 'auth', + 'probe_resp_suppression': 'enable', + 'probe_resp_threshold': 'test_value_64', + 'ptk_rekey': 'enable', + 'ptk_rekey_intv': '66', + 'qos_profile': 'test_value_67', + 'quarantine': 'enable', + 'radio_2g_threshold': 'test_value_69', + 'radio_5g_threshold': 'test_value_70', + 'radio_sensitivity': 'enable', + 'radius_mac_auth': 'enable', + 'radius_mac_auth_server': 'test_value_73', + 'radius_server': 'test_value_74', + 'rates_11a': '1', + 'rates_11ac_ss12': 'mcs0/1', + 'rates_11ac_ss34': 'mcs0/3', + 'rates_11bg': '1', + 'rates_11n_ss12': 'mcs0/1', + 'rates_11n_ss34': 'mcs16/3', + 'schedule': 'test_value_81', + 'security': 'open', + 'security_exempt_list': 'test_value_83', + 'security_obsolete_option': 'enable', + 'security_redirect_url': 'test_value_85', + 'split_tunneling': 'enable', + 'ssid': 'test_value_87', + 'tkip_counter_measure': 'enable', + 'utm_profile': 'test_value_89', + 'vdom': 'test_value_90', + 'vlan_auto': 'enable', + 'vlan_pooling': 'wtp-group', + 'vlanid': '93', + 'voice_enterprise': 'disable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_vap.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'acct-interim-interval': '3', + 'alias': 'test_value_4', + 'auth': 'psk', + 'broadcast-ssid': 'enable', + 'broadcast-suppression': 'dhcp-up', + 'captive-portal-ac-name': 'test_value_8', + 'captive-portal-macauth-radius-secret': 'test_value_9', + 'captive-portal-macauth-radius-server': 'test_value_10', + 'captive-portal-radius-secret': 'test_value_11', + 'captive-portal-radius-server': 'test_value_12', + 'captive-portal-session-timeout-interval': '13', + 'dhcp-lease-time': '14', + 'dhcp-option82-circuit-id-insertion': 'style-1', + 'dhcp-option82-insertion': 'enable', + 'dhcp-option82-remote-id-insertion': 'style-1', + 'dynamic-vlan': 'enable', + 'eap-reauth': 'enable', + 'eap-reauth-intv': '20', + 'eapol-key-retries': 'disable', + 'encrypt': 'TKIP', + 'external-fast-roaming': 'enable', + 'external-logout': 'test_value_24', + 'external-web': 'test_value_25', + 'fast-bss-transition': 'disable', + 'fast-roaming': 'enable', + 'ft-mobility-domain': '28', + 'ft-over-ds': 'disable', + 'ft-r0-key-lifetime': '30', + 'gtk-rekey': 'enable', + 'gtk-rekey-intv': '32', + 'hotspot20-profile': 'test_value_33', + 'intra-vap-privacy': 'enable', + 'ip': 'test_value_35', + 'key': 'test_value_36', + 'keyindex': '37', + 'ldpc': 'disable', + 'local-authentication': 'enable', + 'local-bridging': 'enable', + 'local-lan': 'allow', + 'local-standalone': 'enable', + 'local-standalone-nat': 'enable', + 'mac-auth-bypass': 'enable', + 'mac-filter': 'enable', + 'mac-filter-policy-other': 'allow', + 'max-clients': '47', + 'max-clients-ap': '48', + 'me-disable-thresh': '49', + 'mesh-backhaul': 'enable', + 'mpsk': 'enable', + 'mpsk-concurrent-clients': '52', + 'multicast-enhance': 'enable', + 'multicast-rate': '0', + 'name': 'default_name_55', + 'okc': 'disable', + 'passphrase': 'test_value_57', + 'pmf': 'disable', + 'pmf-assoc-comeback-timeout': '59', + 'pmf-sa-query-retry-timeout': '60', + 'portal-message-override-group': 'test_value_61', + 'portal-type': 'auth', + 'probe-resp-suppression': 'enable', + 'probe-resp-threshold': 'test_value_64', + 'ptk-rekey': 'enable', + 'ptk-rekey-intv': '66', + 'qos-profile': 'test_value_67', + 'quarantine': 'enable', + 'radio-2g-threshold': 'test_value_69', + 'radio-5g-threshold': 'test_value_70', + 'radio-sensitivity': 'enable', + 'radius-mac-auth': 'enable', + 'radius-mac-auth-server': 'test_value_73', + 'radius-server': 'test_value_74', + 'rates-11a': '1', + 'rates-11ac-ss12': 'mcs0/1', + 'rates-11ac-ss34': 'mcs0/3', + 'rates-11bg': '1', + 'rates-11n-ss12': 'mcs0/1', + 'rates-11n-ss34': 'mcs16/3', + 'schedule': 'test_value_81', + 'security': 'open', + 'security-exempt-list': 'test_value_83', + 'security-obsolete-option': 'enable', + 'security-redirect-url': 'test_value_85', + 'split-tunneling': 'enable', + 'ssid': 'test_value_87', + 'tkip-counter-measure': 'enable', + 'utm-profile': 'test_value_89', + 'vdom': 'test_value_90', + 'vlan-auto': 'enable', + 'vlan-pooling': 'wtp-group', + 'vlanid': '93', + 'voice-enterprise': 'disable' + } + + set_method_mock.assert_called_with('wireless-controller', 'vap', 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_wireless_controller_wids_profile.py b/test/units/modules/network/fortios/test_fortios_wireless_controller_wids_profile.py new file mode 100644 index 00000000000..622dbef72e2 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_wireless_controller_wids_profile.py @@ -0,0 +1,679 @@ +# 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_wireless_controller_wids_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_wireless_controller_wids_profile.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_wireless_controller_wids_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', + 'wireless_controller_wids_profile': { + 'ap_auto_suppress': 'enable', + 'ap_bgscan_disable_day': 'sunday', + 'ap_bgscan_disable_end': 'test_value_5', + 'ap_bgscan_disable_start': 'test_value_6', + 'ap_bgscan_duration': '7', + 'ap_bgscan_idle': '8', + 'ap_bgscan_intv': '9', + 'ap_bgscan_period': '10', + 'ap_bgscan_report_intv': '11', + 'ap_fgscan_report_intv': '12', + 'ap_scan': 'disable', + 'ap_scan_passive': 'enable', + 'asleap_attack': 'enable', + 'assoc_flood_thresh': '16', + 'assoc_flood_time': '17', + 'assoc_frame_flood': 'enable', + 'auth_flood_thresh': '19', + 'auth_flood_time': '20', + 'auth_frame_flood': 'enable', + 'comment': 'Comment.', + 'deauth_broadcast': 'enable', + 'deauth_unknown_src_thresh': '24', + 'eapol_fail_flood': 'enable', + 'eapol_fail_intv': '26', + 'eapol_fail_thresh': '27', + 'eapol_logoff_flood': 'enable', + 'eapol_logoff_intv': '29', + 'eapol_logoff_thresh': '30', + 'eapol_pre_fail_flood': 'enable', + 'eapol_pre_fail_intv': '32', + 'eapol_pre_fail_thresh': '33', + 'eapol_pre_succ_flood': 'enable', + 'eapol_pre_succ_intv': '35', + 'eapol_pre_succ_thresh': '36', + 'eapol_start_flood': 'enable', + 'eapol_start_intv': '38', + 'eapol_start_thresh': '39', + 'eapol_succ_flood': 'enable', + 'eapol_succ_intv': '41', + 'eapol_succ_thresh': '42', + 'invalid_mac_oui': 'enable', + 'long_duration_attack': 'enable', + 'long_duration_thresh': '45', + 'name': 'default_name_46', + 'null_ssid_probe_resp': 'enable', + 'sensor_mode': 'disable', + 'spoofed_deauth': 'enable', + 'weak_wep_iv': 'enable', + 'wireless_bridge': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wids_profile.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'ap-auto-suppress': 'enable', + 'ap-bgscan-disable-day': 'sunday', + 'ap-bgscan-disable-end': 'test_value_5', + 'ap-bgscan-disable-start': 'test_value_6', + 'ap-bgscan-duration': '7', + 'ap-bgscan-idle': '8', + 'ap-bgscan-intv': '9', + 'ap-bgscan-period': '10', + 'ap-bgscan-report-intv': '11', + 'ap-fgscan-report-intv': '12', + 'ap-scan': 'disable', + 'ap-scan-passive': 'enable', + 'asleap-attack': 'enable', + 'assoc-flood-thresh': '16', + 'assoc-flood-time': '17', + 'assoc-frame-flood': 'enable', + 'auth-flood-thresh': '19', + 'auth-flood-time': '20', + 'auth-frame-flood': 'enable', + 'comment': 'Comment.', + 'deauth-broadcast': 'enable', + 'deauth-unknown-src-thresh': '24', + 'eapol-fail-flood': 'enable', + 'eapol-fail-intv': '26', + 'eapol-fail-thresh': '27', + 'eapol-logoff-flood': 'enable', + 'eapol-logoff-intv': '29', + 'eapol-logoff-thresh': '30', + 'eapol-pre-fail-flood': 'enable', + 'eapol-pre-fail-intv': '32', + 'eapol-pre-fail-thresh': '33', + 'eapol-pre-succ-flood': 'enable', + 'eapol-pre-succ-intv': '35', + 'eapol-pre-succ-thresh': '36', + 'eapol-start-flood': 'enable', + 'eapol-start-intv': '38', + 'eapol-start-thresh': '39', + 'eapol-succ-flood': 'enable', + 'eapol-succ-intv': '41', + 'eapol-succ-thresh': '42', + 'invalid-mac-oui': 'enable', + 'long-duration-attack': 'enable', + 'long-duration-thresh': '45', + 'name': 'default_name_46', + 'null-ssid-probe-resp': 'enable', + 'sensor-mode': 'disable', + 'spoofed-deauth': 'enable', + 'weak-wep-iv': 'enable', + 'wireless-bridge': 'enable' + } + + set_method_mock.assert_called_with('wireless-controller', 'wids-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_wireless_controller_wids_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', + 'wireless_controller_wids_profile': { + 'ap_auto_suppress': 'enable', + 'ap_bgscan_disable_day': 'sunday', + 'ap_bgscan_disable_end': 'test_value_5', + 'ap_bgscan_disable_start': 'test_value_6', + 'ap_bgscan_duration': '7', + 'ap_bgscan_idle': '8', + 'ap_bgscan_intv': '9', + 'ap_bgscan_period': '10', + 'ap_bgscan_report_intv': '11', + 'ap_fgscan_report_intv': '12', + 'ap_scan': 'disable', + 'ap_scan_passive': 'enable', + 'asleap_attack': 'enable', + 'assoc_flood_thresh': '16', + 'assoc_flood_time': '17', + 'assoc_frame_flood': 'enable', + 'auth_flood_thresh': '19', + 'auth_flood_time': '20', + 'auth_frame_flood': 'enable', + 'comment': 'Comment.', + 'deauth_broadcast': 'enable', + 'deauth_unknown_src_thresh': '24', + 'eapol_fail_flood': 'enable', + 'eapol_fail_intv': '26', + 'eapol_fail_thresh': '27', + 'eapol_logoff_flood': 'enable', + 'eapol_logoff_intv': '29', + 'eapol_logoff_thresh': '30', + 'eapol_pre_fail_flood': 'enable', + 'eapol_pre_fail_intv': '32', + 'eapol_pre_fail_thresh': '33', + 'eapol_pre_succ_flood': 'enable', + 'eapol_pre_succ_intv': '35', + 'eapol_pre_succ_thresh': '36', + 'eapol_start_flood': 'enable', + 'eapol_start_intv': '38', + 'eapol_start_thresh': '39', + 'eapol_succ_flood': 'enable', + 'eapol_succ_intv': '41', + 'eapol_succ_thresh': '42', + 'invalid_mac_oui': 'enable', + 'long_duration_attack': 'enable', + 'long_duration_thresh': '45', + 'name': 'default_name_46', + 'null_ssid_probe_resp': 'enable', + 'sensor_mode': 'disable', + 'spoofed_deauth': 'enable', + 'weak_wep_iv': 'enable', + 'wireless_bridge': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wids_profile.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'ap-auto-suppress': 'enable', + 'ap-bgscan-disable-day': 'sunday', + 'ap-bgscan-disable-end': 'test_value_5', + 'ap-bgscan-disable-start': 'test_value_6', + 'ap-bgscan-duration': '7', + 'ap-bgscan-idle': '8', + 'ap-bgscan-intv': '9', + 'ap-bgscan-period': '10', + 'ap-bgscan-report-intv': '11', + 'ap-fgscan-report-intv': '12', + 'ap-scan': 'disable', + 'ap-scan-passive': 'enable', + 'asleap-attack': 'enable', + 'assoc-flood-thresh': '16', + 'assoc-flood-time': '17', + 'assoc-frame-flood': 'enable', + 'auth-flood-thresh': '19', + 'auth-flood-time': '20', + 'auth-frame-flood': 'enable', + 'comment': 'Comment.', + 'deauth-broadcast': 'enable', + 'deauth-unknown-src-thresh': '24', + 'eapol-fail-flood': 'enable', + 'eapol-fail-intv': '26', + 'eapol-fail-thresh': '27', + 'eapol-logoff-flood': 'enable', + 'eapol-logoff-intv': '29', + 'eapol-logoff-thresh': '30', + 'eapol-pre-fail-flood': 'enable', + 'eapol-pre-fail-intv': '32', + 'eapol-pre-fail-thresh': '33', + 'eapol-pre-succ-flood': 'enable', + 'eapol-pre-succ-intv': '35', + 'eapol-pre-succ-thresh': '36', + 'eapol-start-flood': 'enable', + 'eapol-start-intv': '38', + 'eapol-start-thresh': '39', + 'eapol-succ-flood': 'enable', + 'eapol-succ-intv': '41', + 'eapol-succ-thresh': '42', + 'invalid-mac-oui': 'enable', + 'long-duration-attack': 'enable', + 'long-duration-thresh': '45', + 'name': 'default_name_46', + 'null-ssid-probe-resp': 'enable', + 'sensor-mode': 'disable', + 'spoofed-deauth': 'enable', + 'weak-wep-iv': 'enable', + 'wireless-bridge': 'enable' + } + + set_method_mock.assert_called_with('wireless-controller', 'wids-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_wireless_controller_wids_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', + 'wireless_controller_wids_profile': { + 'ap_auto_suppress': 'enable', + 'ap_bgscan_disable_day': 'sunday', + 'ap_bgscan_disable_end': 'test_value_5', + 'ap_bgscan_disable_start': 'test_value_6', + 'ap_bgscan_duration': '7', + 'ap_bgscan_idle': '8', + 'ap_bgscan_intv': '9', + 'ap_bgscan_period': '10', + 'ap_bgscan_report_intv': '11', + 'ap_fgscan_report_intv': '12', + 'ap_scan': 'disable', + 'ap_scan_passive': 'enable', + 'asleap_attack': 'enable', + 'assoc_flood_thresh': '16', + 'assoc_flood_time': '17', + 'assoc_frame_flood': 'enable', + 'auth_flood_thresh': '19', + 'auth_flood_time': '20', + 'auth_frame_flood': 'enable', + 'comment': 'Comment.', + 'deauth_broadcast': 'enable', + 'deauth_unknown_src_thresh': '24', + 'eapol_fail_flood': 'enable', + 'eapol_fail_intv': '26', + 'eapol_fail_thresh': '27', + 'eapol_logoff_flood': 'enable', + 'eapol_logoff_intv': '29', + 'eapol_logoff_thresh': '30', + 'eapol_pre_fail_flood': 'enable', + 'eapol_pre_fail_intv': '32', + 'eapol_pre_fail_thresh': '33', + 'eapol_pre_succ_flood': 'enable', + 'eapol_pre_succ_intv': '35', + 'eapol_pre_succ_thresh': '36', + 'eapol_start_flood': 'enable', + 'eapol_start_intv': '38', + 'eapol_start_thresh': '39', + 'eapol_succ_flood': 'enable', + 'eapol_succ_intv': '41', + 'eapol_succ_thresh': '42', + 'invalid_mac_oui': 'enable', + 'long_duration_attack': 'enable', + 'long_duration_thresh': '45', + 'name': 'default_name_46', + 'null_ssid_probe_resp': 'enable', + 'sensor_mode': 'disable', + 'spoofed_deauth': 'enable', + 'weak_wep_iv': 'enable', + 'wireless_bridge': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wids_profile.fortios_wireless_controller(input_data, fos_instance) + + delete_method_mock.assert_called_with('wireless-controller', 'wids-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_wireless_controller_wids_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', + 'wireless_controller_wids_profile': { + 'ap_auto_suppress': 'enable', + 'ap_bgscan_disable_day': 'sunday', + 'ap_bgscan_disable_end': 'test_value_5', + 'ap_bgscan_disable_start': 'test_value_6', + 'ap_bgscan_duration': '7', + 'ap_bgscan_idle': '8', + 'ap_bgscan_intv': '9', + 'ap_bgscan_period': '10', + 'ap_bgscan_report_intv': '11', + 'ap_fgscan_report_intv': '12', + 'ap_scan': 'disable', + 'ap_scan_passive': 'enable', + 'asleap_attack': 'enable', + 'assoc_flood_thresh': '16', + 'assoc_flood_time': '17', + 'assoc_frame_flood': 'enable', + 'auth_flood_thresh': '19', + 'auth_flood_time': '20', + 'auth_frame_flood': 'enable', + 'comment': 'Comment.', + 'deauth_broadcast': 'enable', + 'deauth_unknown_src_thresh': '24', + 'eapol_fail_flood': 'enable', + 'eapol_fail_intv': '26', + 'eapol_fail_thresh': '27', + 'eapol_logoff_flood': 'enable', + 'eapol_logoff_intv': '29', + 'eapol_logoff_thresh': '30', + 'eapol_pre_fail_flood': 'enable', + 'eapol_pre_fail_intv': '32', + 'eapol_pre_fail_thresh': '33', + 'eapol_pre_succ_flood': 'enable', + 'eapol_pre_succ_intv': '35', + 'eapol_pre_succ_thresh': '36', + 'eapol_start_flood': 'enable', + 'eapol_start_intv': '38', + 'eapol_start_thresh': '39', + 'eapol_succ_flood': 'enable', + 'eapol_succ_intv': '41', + 'eapol_succ_thresh': '42', + 'invalid_mac_oui': 'enable', + 'long_duration_attack': 'enable', + 'long_duration_thresh': '45', + 'name': 'default_name_46', + 'null_ssid_probe_resp': 'enable', + 'sensor_mode': 'disable', + 'spoofed_deauth': 'enable', + 'weak_wep_iv': 'enable', + 'wireless_bridge': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wids_profile.fortios_wireless_controller(input_data, fos_instance) + + delete_method_mock.assert_called_with('wireless-controller', 'wids-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_wireless_controller_wids_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', + 'wireless_controller_wids_profile': { + 'ap_auto_suppress': 'enable', + 'ap_bgscan_disable_day': 'sunday', + 'ap_bgscan_disable_end': 'test_value_5', + 'ap_bgscan_disable_start': 'test_value_6', + 'ap_bgscan_duration': '7', + 'ap_bgscan_idle': '8', + 'ap_bgscan_intv': '9', + 'ap_bgscan_period': '10', + 'ap_bgscan_report_intv': '11', + 'ap_fgscan_report_intv': '12', + 'ap_scan': 'disable', + 'ap_scan_passive': 'enable', + 'asleap_attack': 'enable', + 'assoc_flood_thresh': '16', + 'assoc_flood_time': '17', + 'assoc_frame_flood': 'enable', + 'auth_flood_thresh': '19', + 'auth_flood_time': '20', + 'auth_frame_flood': 'enable', + 'comment': 'Comment.', + 'deauth_broadcast': 'enable', + 'deauth_unknown_src_thresh': '24', + 'eapol_fail_flood': 'enable', + 'eapol_fail_intv': '26', + 'eapol_fail_thresh': '27', + 'eapol_logoff_flood': 'enable', + 'eapol_logoff_intv': '29', + 'eapol_logoff_thresh': '30', + 'eapol_pre_fail_flood': 'enable', + 'eapol_pre_fail_intv': '32', + 'eapol_pre_fail_thresh': '33', + 'eapol_pre_succ_flood': 'enable', + 'eapol_pre_succ_intv': '35', + 'eapol_pre_succ_thresh': '36', + 'eapol_start_flood': 'enable', + 'eapol_start_intv': '38', + 'eapol_start_thresh': '39', + 'eapol_succ_flood': 'enable', + 'eapol_succ_intv': '41', + 'eapol_succ_thresh': '42', + 'invalid_mac_oui': 'enable', + 'long_duration_attack': 'enable', + 'long_duration_thresh': '45', + 'name': 'default_name_46', + 'null_ssid_probe_resp': 'enable', + 'sensor_mode': 'disable', + 'spoofed_deauth': 'enable', + 'weak_wep_iv': 'enable', + 'wireless_bridge': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wids_profile.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'ap-auto-suppress': 'enable', + 'ap-bgscan-disable-day': 'sunday', + 'ap-bgscan-disable-end': 'test_value_5', + 'ap-bgscan-disable-start': 'test_value_6', + 'ap-bgscan-duration': '7', + 'ap-bgscan-idle': '8', + 'ap-bgscan-intv': '9', + 'ap-bgscan-period': '10', + 'ap-bgscan-report-intv': '11', + 'ap-fgscan-report-intv': '12', + 'ap-scan': 'disable', + 'ap-scan-passive': 'enable', + 'asleap-attack': 'enable', + 'assoc-flood-thresh': '16', + 'assoc-flood-time': '17', + 'assoc-frame-flood': 'enable', + 'auth-flood-thresh': '19', + 'auth-flood-time': '20', + 'auth-frame-flood': 'enable', + 'comment': 'Comment.', + 'deauth-broadcast': 'enable', + 'deauth-unknown-src-thresh': '24', + 'eapol-fail-flood': 'enable', + 'eapol-fail-intv': '26', + 'eapol-fail-thresh': '27', + 'eapol-logoff-flood': 'enable', + 'eapol-logoff-intv': '29', + 'eapol-logoff-thresh': '30', + 'eapol-pre-fail-flood': 'enable', + 'eapol-pre-fail-intv': '32', + 'eapol-pre-fail-thresh': '33', + 'eapol-pre-succ-flood': 'enable', + 'eapol-pre-succ-intv': '35', + 'eapol-pre-succ-thresh': '36', + 'eapol-start-flood': 'enable', + 'eapol-start-intv': '38', + 'eapol-start-thresh': '39', + 'eapol-succ-flood': 'enable', + 'eapol-succ-intv': '41', + 'eapol-succ-thresh': '42', + 'invalid-mac-oui': 'enable', + 'long-duration-attack': 'enable', + 'long-duration-thresh': '45', + 'name': 'default_name_46', + 'null-ssid-probe-resp': 'enable', + 'sensor-mode': 'disable', + 'spoofed-deauth': 'enable', + 'weak-wep-iv': 'enable', + 'wireless-bridge': 'enable' + } + + set_method_mock.assert_called_with('wireless-controller', 'wids-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_wireless_controller_wids_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', + 'wireless_controller_wids_profile': { + 'random_attribute_not_valid': 'tag', + 'ap_auto_suppress': 'enable', + 'ap_bgscan_disable_day': 'sunday', + 'ap_bgscan_disable_end': 'test_value_5', + 'ap_bgscan_disable_start': 'test_value_6', + 'ap_bgscan_duration': '7', + 'ap_bgscan_idle': '8', + 'ap_bgscan_intv': '9', + 'ap_bgscan_period': '10', + 'ap_bgscan_report_intv': '11', + 'ap_fgscan_report_intv': '12', + 'ap_scan': 'disable', + 'ap_scan_passive': 'enable', + 'asleap_attack': 'enable', + 'assoc_flood_thresh': '16', + 'assoc_flood_time': '17', + 'assoc_frame_flood': 'enable', + 'auth_flood_thresh': '19', + 'auth_flood_time': '20', + 'auth_frame_flood': 'enable', + 'comment': 'Comment.', + 'deauth_broadcast': 'enable', + 'deauth_unknown_src_thresh': '24', + 'eapol_fail_flood': 'enable', + 'eapol_fail_intv': '26', + 'eapol_fail_thresh': '27', + 'eapol_logoff_flood': 'enable', + 'eapol_logoff_intv': '29', + 'eapol_logoff_thresh': '30', + 'eapol_pre_fail_flood': 'enable', + 'eapol_pre_fail_intv': '32', + 'eapol_pre_fail_thresh': '33', + 'eapol_pre_succ_flood': 'enable', + 'eapol_pre_succ_intv': '35', + 'eapol_pre_succ_thresh': '36', + 'eapol_start_flood': 'enable', + 'eapol_start_intv': '38', + 'eapol_start_thresh': '39', + 'eapol_succ_flood': 'enable', + 'eapol_succ_intv': '41', + 'eapol_succ_thresh': '42', + 'invalid_mac_oui': 'enable', + 'long_duration_attack': 'enable', + 'long_duration_thresh': '45', + 'name': 'default_name_46', + 'null_ssid_probe_resp': 'enable', + 'sensor_mode': 'disable', + 'spoofed_deauth': 'enable', + 'weak_wep_iv': 'enable', + 'wireless_bridge': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wids_profile.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'ap-auto-suppress': 'enable', + 'ap-bgscan-disable-day': 'sunday', + 'ap-bgscan-disable-end': 'test_value_5', + 'ap-bgscan-disable-start': 'test_value_6', + 'ap-bgscan-duration': '7', + 'ap-bgscan-idle': '8', + 'ap-bgscan-intv': '9', + 'ap-bgscan-period': '10', + 'ap-bgscan-report-intv': '11', + 'ap-fgscan-report-intv': '12', + 'ap-scan': 'disable', + 'ap-scan-passive': 'enable', + 'asleap-attack': 'enable', + 'assoc-flood-thresh': '16', + 'assoc-flood-time': '17', + 'assoc-frame-flood': 'enable', + 'auth-flood-thresh': '19', + 'auth-flood-time': '20', + 'auth-frame-flood': 'enable', + 'comment': 'Comment.', + 'deauth-broadcast': 'enable', + 'deauth-unknown-src-thresh': '24', + 'eapol-fail-flood': 'enable', + 'eapol-fail-intv': '26', + 'eapol-fail-thresh': '27', + 'eapol-logoff-flood': 'enable', + 'eapol-logoff-intv': '29', + 'eapol-logoff-thresh': '30', + 'eapol-pre-fail-flood': 'enable', + 'eapol-pre-fail-intv': '32', + 'eapol-pre-fail-thresh': '33', + 'eapol-pre-succ-flood': 'enable', + 'eapol-pre-succ-intv': '35', + 'eapol-pre-succ-thresh': '36', + 'eapol-start-flood': 'enable', + 'eapol-start-intv': '38', + 'eapol-start-thresh': '39', + 'eapol-succ-flood': 'enable', + 'eapol-succ-intv': '41', + 'eapol-succ-thresh': '42', + 'invalid-mac-oui': 'enable', + 'long-duration-attack': 'enable', + 'long-duration-thresh': '45', + 'name': 'default_name_46', + 'null-ssid-probe-resp': 'enable', + 'sensor-mode': 'disable', + 'spoofed-deauth': 'enable', + 'weak-wep-iv': 'enable', + 'wireless-bridge': 'enable' + } + + set_method_mock.assert_called_with('wireless-controller', 'wids-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_wireless_controller_wtp.py b/test/units/modules/network/fortios/test_fortios_wireless_controller_wtp.py new file mode 100644 index 00000000000..aea02faba54 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_wireless_controller_wtp.py @@ -0,0 +1,509 @@ +# 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_wireless_controller_wtp +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_wireless_controller_wtp.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_wireless_controller_wtp_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', + 'wireless_controller_wtp': { + 'admin': 'discovered', + 'allowaccess': 'telnet', + 'bonjour_profile': 'test_value_5', + 'coordinate_enable': 'enable', + 'coordinate_latitude': 'test_value_7', + 'coordinate_longitude': 'test_value_8', + 'coordinate_x': 'test_value_9', + 'coordinate_y': 'test_value_10', + 'image_download': 'enable', + 'index': '12', + 'ip_fragment_preventing': 'tcp-mss-adjust', + 'led_state': 'enable', + 'location': 'test_value_15', + 'login_passwd': 'test_value_16', + 'login_passwd_change': 'yes', + 'mesh_bridge_enable': 'default', + 'name': 'default_name_19', + 'override_allowaccess': 'enable', + 'override_ip_fragment': 'enable', + 'override_lan': 'enable', + 'override_led_state': 'enable', + 'override_login_passwd_change': 'enable', + 'override_split_tunnel': 'enable', + 'override_wan_port_mode': 'enable', + 'split_tunneling_acl_local_ap_subnet': 'enable', + 'split_tunneling_acl_path': 'tunnel', + 'tun_mtu_downlink': '29', + 'tun_mtu_uplink': '30', + 'wan_port_mode': 'wan-lan', + 'wtp_id': 'test_value_32', + 'wtp_mode': 'normal', + 'wtp_profile': 'test_value_34' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wtp.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'admin': 'discovered', + 'allowaccess': 'telnet', + 'bonjour-profile': 'test_value_5', + 'coordinate-enable': 'enable', + 'coordinate-latitude': 'test_value_7', + 'coordinate-longitude': 'test_value_8', + 'coordinate-x': 'test_value_9', + 'coordinate-y': 'test_value_10', + 'image-download': 'enable', + 'index': '12', + 'ip-fragment-preventing': 'tcp-mss-adjust', + 'led-state': 'enable', + 'location': 'test_value_15', + 'login-passwd': 'test_value_16', + 'login-passwd-change': 'yes', + 'mesh-bridge-enable': 'default', + 'name': 'default_name_19', + 'override-allowaccess': 'enable', + 'override-ip-fragment': 'enable', + 'override-lan': 'enable', + 'override-led-state': 'enable', + 'override-login-passwd-change': 'enable', + 'override-split-tunnel': 'enable', + 'override-wan-port-mode': 'enable', + 'split-tunneling-acl-local-ap-subnet': 'enable', + 'split-tunneling-acl-path': 'tunnel', + 'tun-mtu-downlink': '29', + 'tun-mtu-uplink': '30', + 'wan-port-mode': 'wan-lan', + 'wtp-id': 'test_value_32', + 'wtp-mode': 'normal', + 'wtp-profile': 'test_value_34' + } + + set_method_mock.assert_called_with('wireless-controller', 'wtp', 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_wireless_controller_wtp_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', + 'wireless_controller_wtp': { + 'admin': 'discovered', + 'allowaccess': 'telnet', + 'bonjour_profile': 'test_value_5', + 'coordinate_enable': 'enable', + 'coordinate_latitude': 'test_value_7', + 'coordinate_longitude': 'test_value_8', + 'coordinate_x': 'test_value_9', + 'coordinate_y': 'test_value_10', + 'image_download': 'enable', + 'index': '12', + 'ip_fragment_preventing': 'tcp-mss-adjust', + 'led_state': 'enable', + 'location': 'test_value_15', + 'login_passwd': 'test_value_16', + 'login_passwd_change': 'yes', + 'mesh_bridge_enable': 'default', + 'name': 'default_name_19', + 'override_allowaccess': 'enable', + 'override_ip_fragment': 'enable', + 'override_lan': 'enable', + 'override_led_state': 'enable', + 'override_login_passwd_change': 'enable', + 'override_split_tunnel': 'enable', + 'override_wan_port_mode': 'enable', + 'split_tunneling_acl_local_ap_subnet': 'enable', + 'split_tunneling_acl_path': 'tunnel', + 'tun_mtu_downlink': '29', + 'tun_mtu_uplink': '30', + 'wan_port_mode': 'wan-lan', + 'wtp_id': 'test_value_32', + 'wtp_mode': 'normal', + 'wtp_profile': 'test_value_34' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wtp.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'admin': 'discovered', + 'allowaccess': 'telnet', + 'bonjour-profile': 'test_value_5', + 'coordinate-enable': 'enable', + 'coordinate-latitude': 'test_value_7', + 'coordinate-longitude': 'test_value_8', + 'coordinate-x': 'test_value_9', + 'coordinate-y': 'test_value_10', + 'image-download': 'enable', + 'index': '12', + 'ip-fragment-preventing': 'tcp-mss-adjust', + 'led-state': 'enable', + 'location': 'test_value_15', + 'login-passwd': 'test_value_16', + 'login-passwd-change': 'yes', + 'mesh-bridge-enable': 'default', + 'name': 'default_name_19', + 'override-allowaccess': 'enable', + 'override-ip-fragment': 'enable', + 'override-lan': 'enable', + 'override-led-state': 'enable', + 'override-login-passwd-change': 'enable', + 'override-split-tunnel': 'enable', + 'override-wan-port-mode': 'enable', + 'split-tunneling-acl-local-ap-subnet': 'enable', + 'split-tunneling-acl-path': 'tunnel', + 'tun-mtu-downlink': '29', + 'tun-mtu-uplink': '30', + 'wan-port-mode': 'wan-lan', + 'wtp-id': 'test_value_32', + 'wtp-mode': 'normal', + 'wtp-profile': 'test_value_34' + } + + set_method_mock.assert_called_with('wireless-controller', 'wtp', 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_wireless_controller_wtp_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', + 'wireless_controller_wtp': { + 'admin': 'discovered', + 'allowaccess': 'telnet', + 'bonjour_profile': 'test_value_5', + 'coordinate_enable': 'enable', + 'coordinate_latitude': 'test_value_7', + 'coordinate_longitude': 'test_value_8', + 'coordinate_x': 'test_value_9', + 'coordinate_y': 'test_value_10', + 'image_download': 'enable', + 'index': '12', + 'ip_fragment_preventing': 'tcp-mss-adjust', + 'led_state': 'enable', + 'location': 'test_value_15', + 'login_passwd': 'test_value_16', + 'login_passwd_change': 'yes', + 'mesh_bridge_enable': 'default', + 'name': 'default_name_19', + 'override_allowaccess': 'enable', + 'override_ip_fragment': 'enable', + 'override_lan': 'enable', + 'override_led_state': 'enable', + 'override_login_passwd_change': 'enable', + 'override_split_tunnel': 'enable', + 'override_wan_port_mode': 'enable', + 'split_tunneling_acl_local_ap_subnet': 'enable', + 'split_tunneling_acl_path': 'tunnel', + 'tun_mtu_downlink': '29', + 'tun_mtu_uplink': '30', + 'wan_port_mode': 'wan-lan', + 'wtp_id': 'test_value_32', + 'wtp_mode': 'normal', + 'wtp_profile': 'test_value_34' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wtp.fortios_wireless_controller(input_data, fos_instance) + + delete_method_mock.assert_called_with('wireless-controller', 'wtp', 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_wireless_controller_wtp_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', + 'wireless_controller_wtp': { + 'admin': 'discovered', + 'allowaccess': 'telnet', + 'bonjour_profile': 'test_value_5', + 'coordinate_enable': 'enable', + 'coordinate_latitude': 'test_value_7', + 'coordinate_longitude': 'test_value_8', + 'coordinate_x': 'test_value_9', + 'coordinate_y': 'test_value_10', + 'image_download': 'enable', + 'index': '12', + 'ip_fragment_preventing': 'tcp-mss-adjust', + 'led_state': 'enable', + 'location': 'test_value_15', + 'login_passwd': 'test_value_16', + 'login_passwd_change': 'yes', + 'mesh_bridge_enable': 'default', + 'name': 'default_name_19', + 'override_allowaccess': 'enable', + 'override_ip_fragment': 'enable', + 'override_lan': 'enable', + 'override_led_state': 'enable', + 'override_login_passwd_change': 'enable', + 'override_split_tunnel': 'enable', + 'override_wan_port_mode': 'enable', + 'split_tunneling_acl_local_ap_subnet': 'enable', + 'split_tunneling_acl_path': 'tunnel', + 'tun_mtu_downlink': '29', + 'tun_mtu_uplink': '30', + 'wan_port_mode': 'wan-lan', + 'wtp_id': 'test_value_32', + 'wtp_mode': 'normal', + 'wtp_profile': 'test_value_34' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wtp.fortios_wireless_controller(input_data, fos_instance) + + delete_method_mock.assert_called_with('wireless-controller', 'wtp', 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_wireless_controller_wtp_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', + 'wireless_controller_wtp': { + 'admin': 'discovered', + 'allowaccess': 'telnet', + 'bonjour_profile': 'test_value_5', + 'coordinate_enable': 'enable', + 'coordinate_latitude': 'test_value_7', + 'coordinate_longitude': 'test_value_8', + 'coordinate_x': 'test_value_9', + 'coordinate_y': 'test_value_10', + 'image_download': 'enable', + 'index': '12', + 'ip_fragment_preventing': 'tcp-mss-adjust', + 'led_state': 'enable', + 'location': 'test_value_15', + 'login_passwd': 'test_value_16', + 'login_passwd_change': 'yes', + 'mesh_bridge_enable': 'default', + 'name': 'default_name_19', + 'override_allowaccess': 'enable', + 'override_ip_fragment': 'enable', + 'override_lan': 'enable', + 'override_led_state': 'enable', + 'override_login_passwd_change': 'enable', + 'override_split_tunnel': 'enable', + 'override_wan_port_mode': 'enable', + 'split_tunneling_acl_local_ap_subnet': 'enable', + 'split_tunneling_acl_path': 'tunnel', + 'tun_mtu_downlink': '29', + 'tun_mtu_uplink': '30', + 'wan_port_mode': 'wan-lan', + 'wtp_id': 'test_value_32', + 'wtp_mode': 'normal', + 'wtp_profile': 'test_value_34' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wtp.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'admin': 'discovered', + 'allowaccess': 'telnet', + 'bonjour-profile': 'test_value_5', + 'coordinate-enable': 'enable', + 'coordinate-latitude': 'test_value_7', + 'coordinate-longitude': 'test_value_8', + 'coordinate-x': 'test_value_9', + 'coordinate-y': 'test_value_10', + 'image-download': 'enable', + 'index': '12', + 'ip-fragment-preventing': 'tcp-mss-adjust', + 'led-state': 'enable', + 'location': 'test_value_15', + 'login-passwd': 'test_value_16', + 'login-passwd-change': 'yes', + 'mesh-bridge-enable': 'default', + 'name': 'default_name_19', + 'override-allowaccess': 'enable', + 'override-ip-fragment': 'enable', + 'override-lan': 'enable', + 'override-led-state': 'enable', + 'override-login-passwd-change': 'enable', + 'override-split-tunnel': 'enable', + 'override-wan-port-mode': 'enable', + 'split-tunneling-acl-local-ap-subnet': 'enable', + 'split-tunneling-acl-path': 'tunnel', + 'tun-mtu-downlink': '29', + 'tun-mtu-uplink': '30', + 'wan-port-mode': 'wan-lan', + 'wtp-id': 'test_value_32', + 'wtp-mode': 'normal', + 'wtp-profile': 'test_value_34' + } + + set_method_mock.assert_called_with('wireless-controller', 'wtp', 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_wireless_controller_wtp_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', + 'wireless_controller_wtp': { + 'random_attribute_not_valid': 'tag', + 'admin': 'discovered', + 'allowaccess': 'telnet', + 'bonjour_profile': 'test_value_5', + 'coordinate_enable': 'enable', + 'coordinate_latitude': 'test_value_7', + 'coordinate_longitude': 'test_value_8', + 'coordinate_x': 'test_value_9', + 'coordinate_y': 'test_value_10', + 'image_download': 'enable', + 'index': '12', + 'ip_fragment_preventing': 'tcp-mss-adjust', + 'led_state': 'enable', + 'location': 'test_value_15', + 'login_passwd': 'test_value_16', + 'login_passwd_change': 'yes', + 'mesh_bridge_enable': 'default', + 'name': 'default_name_19', + 'override_allowaccess': 'enable', + 'override_ip_fragment': 'enable', + 'override_lan': 'enable', + 'override_led_state': 'enable', + 'override_login_passwd_change': 'enable', + 'override_split_tunnel': 'enable', + 'override_wan_port_mode': 'enable', + 'split_tunneling_acl_local_ap_subnet': 'enable', + 'split_tunneling_acl_path': 'tunnel', + 'tun_mtu_downlink': '29', + 'tun_mtu_uplink': '30', + 'wan_port_mode': 'wan-lan', + 'wtp_id': 'test_value_32', + 'wtp_mode': 'normal', + 'wtp_profile': 'test_value_34' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wtp.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'admin': 'discovered', + 'allowaccess': 'telnet', + 'bonjour-profile': 'test_value_5', + 'coordinate-enable': 'enable', + 'coordinate-latitude': 'test_value_7', + 'coordinate-longitude': 'test_value_8', + 'coordinate-x': 'test_value_9', + 'coordinate-y': 'test_value_10', + 'image-download': 'enable', + 'index': '12', + 'ip-fragment-preventing': 'tcp-mss-adjust', + 'led-state': 'enable', + 'location': 'test_value_15', + 'login-passwd': 'test_value_16', + 'login-passwd-change': 'yes', + 'mesh-bridge-enable': 'default', + 'name': 'default_name_19', + 'override-allowaccess': 'enable', + 'override-ip-fragment': 'enable', + 'override-lan': 'enable', + 'override-led-state': 'enable', + 'override-login-passwd-change': 'enable', + 'override-split-tunnel': 'enable', + 'override-wan-port-mode': 'enable', + 'split-tunneling-acl-local-ap-subnet': 'enable', + 'split-tunneling-acl-path': 'tunnel', + 'tun-mtu-downlink': '29', + 'tun-mtu-uplink': '30', + 'wan-port-mode': 'wan-lan', + 'wtp-id': 'test_value_32', + 'wtp-mode': 'normal', + 'wtp-profile': 'test_value_34' + } + + set_method_mock.assert_called_with('wireless-controller', 'wtp', 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_wireless_controller_wtp_profile.py b/test/units/modules/network/fortios/test_fortios_wireless_controller_wtp_profile.py new file mode 100644 index 00000000000..d578db58021 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_wireless_controller_wtp_profile.py @@ -0,0 +1,439 @@ +# 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_wireless_controller_wtp_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_wireless_controller_wtp_profile.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_wireless_controller_wtp_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', + 'wireless_controller_wtp_profile': { + 'allowaccess': 'telnet', + 'ap_country': 'NA', + 'ble_profile': 'test_value_5', + 'comment': 'Comment.', + 'control_message_offload': 'ebp-frame', + 'dtls_in_kernel': 'enable', + 'dtls_policy': 'clear-text', + 'energy_efficient_ethernet': 'enable', + 'ext_info_enable': 'enable', + 'handoff_roaming': 'enable', + 'handoff_rssi': '13', + 'handoff_sta_thresh': '14', + 'ip_fragment_preventing': 'tcp-mss-adjust', + 'led_state': 'enable', + 'lldp': 'enable', + 'login_passwd': 'test_value_18', + 'login_passwd_change': 'yes', + 'max_clients': '20', + 'name': 'default_name_21', + 'poe_mode': 'auto', + 'split_tunneling_acl_local_ap_subnet': 'enable', + 'split_tunneling_acl_path': 'tunnel', + 'tun_mtu_downlink': '25', + 'tun_mtu_uplink': '26', + 'wan_port_mode': 'wan-lan' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wtp_profile.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'allowaccess': 'telnet', + 'ap-country': 'NA', + 'ble-profile': 'test_value_5', + 'comment': 'Comment.', + 'control-message-offload': 'ebp-frame', + 'dtls-in-kernel': 'enable', + 'dtls-policy': 'clear-text', + 'energy-efficient-ethernet': 'enable', + 'ext-info-enable': 'enable', + 'handoff-roaming': 'enable', + 'handoff-rssi': '13', + 'handoff-sta-thresh': '14', + 'ip-fragment-preventing': 'tcp-mss-adjust', + 'led-state': 'enable', + 'lldp': 'enable', + 'login-passwd': 'test_value_18', + 'login-passwd-change': 'yes', + 'max-clients': '20', + 'name': 'default_name_21', + 'poe-mode': 'auto', + 'split-tunneling-acl-local-ap-subnet': 'enable', + 'split-tunneling-acl-path': 'tunnel', + 'tun-mtu-downlink': '25', + 'tun-mtu-uplink': '26', + 'wan-port-mode': 'wan-lan' + } + + set_method_mock.assert_called_with('wireless-controller', 'wtp-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_wireless_controller_wtp_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', + 'wireless_controller_wtp_profile': { + 'allowaccess': 'telnet', + 'ap_country': 'NA', + 'ble_profile': 'test_value_5', + 'comment': 'Comment.', + 'control_message_offload': 'ebp-frame', + 'dtls_in_kernel': 'enable', + 'dtls_policy': 'clear-text', + 'energy_efficient_ethernet': 'enable', + 'ext_info_enable': 'enable', + 'handoff_roaming': 'enable', + 'handoff_rssi': '13', + 'handoff_sta_thresh': '14', + 'ip_fragment_preventing': 'tcp-mss-adjust', + 'led_state': 'enable', + 'lldp': 'enable', + 'login_passwd': 'test_value_18', + 'login_passwd_change': 'yes', + 'max_clients': '20', + 'name': 'default_name_21', + 'poe_mode': 'auto', + 'split_tunneling_acl_local_ap_subnet': 'enable', + 'split_tunneling_acl_path': 'tunnel', + 'tun_mtu_downlink': '25', + 'tun_mtu_uplink': '26', + 'wan_port_mode': 'wan-lan' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wtp_profile.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'allowaccess': 'telnet', + 'ap-country': 'NA', + 'ble-profile': 'test_value_5', + 'comment': 'Comment.', + 'control-message-offload': 'ebp-frame', + 'dtls-in-kernel': 'enable', + 'dtls-policy': 'clear-text', + 'energy-efficient-ethernet': 'enable', + 'ext-info-enable': 'enable', + 'handoff-roaming': 'enable', + 'handoff-rssi': '13', + 'handoff-sta-thresh': '14', + 'ip-fragment-preventing': 'tcp-mss-adjust', + 'led-state': 'enable', + 'lldp': 'enable', + 'login-passwd': 'test_value_18', + 'login-passwd-change': 'yes', + 'max-clients': '20', + 'name': 'default_name_21', + 'poe-mode': 'auto', + 'split-tunneling-acl-local-ap-subnet': 'enable', + 'split-tunneling-acl-path': 'tunnel', + 'tun-mtu-downlink': '25', + 'tun-mtu-uplink': '26', + 'wan-port-mode': 'wan-lan' + } + + set_method_mock.assert_called_with('wireless-controller', 'wtp-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_wireless_controller_wtp_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', + 'wireless_controller_wtp_profile': { + 'allowaccess': 'telnet', + 'ap_country': 'NA', + 'ble_profile': 'test_value_5', + 'comment': 'Comment.', + 'control_message_offload': 'ebp-frame', + 'dtls_in_kernel': 'enable', + 'dtls_policy': 'clear-text', + 'energy_efficient_ethernet': 'enable', + 'ext_info_enable': 'enable', + 'handoff_roaming': 'enable', + 'handoff_rssi': '13', + 'handoff_sta_thresh': '14', + 'ip_fragment_preventing': 'tcp-mss-adjust', + 'led_state': 'enable', + 'lldp': 'enable', + 'login_passwd': 'test_value_18', + 'login_passwd_change': 'yes', + 'max_clients': '20', + 'name': 'default_name_21', + 'poe_mode': 'auto', + 'split_tunneling_acl_local_ap_subnet': 'enable', + 'split_tunneling_acl_path': 'tunnel', + 'tun_mtu_downlink': '25', + 'tun_mtu_uplink': '26', + 'wan_port_mode': 'wan-lan' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wtp_profile.fortios_wireless_controller(input_data, fos_instance) + + delete_method_mock.assert_called_with('wireless-controller', 'wtp-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_wireless_controller_wtp_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', + 'wireless_controller_wtp_profile': { + 'allowaccess': 'telnet', + 'ap_country': 'NA', + 'ble_profile': 'test_value_5', + 'comment': 'Comment.', + 'control_message_offload': 'ebp-frame', + 'dtls_in_kernel': 'enable', + 'dtls_policy': 'clear-text', + 'energy_efficient_ethernet': 'enable', + 'ext_info_enable': 'enable', + 'handoff_roaming': 'enable', + 'handoff_rssi': '13', + 'handoff_sta_thresh': '14', + 'ip_fragment_preventing': 'tcp-mss-adjust', + 'led_state': 'enable', + 'lldp': 'enable', + 'login_passwd': 'test_value_18', + 'login_passwd_change': 'yes', + 'max_clients': '20', + 'name': 'default_name_21', + 'poe_mode': 'auto', + 'split_tunneling_acl_local_ap_subnet': 'enable', + 'split_tunneling_acl_path': 'tunnel', + 'tun_mtu_downlink': '25', + 'tun_mtu_uplink': '26', + 'wan_port_mode': 'wan-lan' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wtp_profile.fortios_wireless_controller(input_data, fos_instance) + + delete_method_mock.assert_called_with('wireless-controller', 'wtp-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_wireless_controller_wtp_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', + 'wireless_controller_wtp_profile': { + 'allowaccess': 'telnet', + 'ap_country': 'NA', + 'ble_profile': 'test_value_5', + 'comment': 'Comment.', + 'control_message_offload': 'ebp-frame', + 'dtls_in_kernel': 'enable', + 'dtls_policy': 'clear-text', + 'energy_efficient_ethernet': 'enable', + 'ext_info_enable': 'enable', + 'handoff_roaming': 'enable', + 'handoff_rssi': '13', + 'handoff_sta_thresh': '14', + 'ip_fragment_preventing': 'tcp-mss-adjust', + 'led_state': 'enable', + 'lldp': 'enable', + 'login_passwd': 'test_value_18', + 'login_passwd_change': 'yes', + 'max_clients': '20', + 'name': 'default_name_21', + 'poe_mode': 'auto', + 'split_tunneling_acl_local_ap_subnet': 'enable', + 'split_tunneling_acl_path': 'tunnel', + 'tun_mtu_downlink': '25', + 'tun_mtu_uplink': '26', + 'wan_port_mode': 'wan-lan' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wtp_profile.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'allowaccess': 'telnet', + 'ap-country': 'NA', + 'ble-profile': 'test_value_5', + 'comment': 'Comment.', + 'control-message-offload': 'ebp-frame', + 'dtls-in-kernel': 'enable', + 'dtls-policy': 'clear-text', + 'energy-efficient-ethernet': 'enable', + 'ext-info-enable': 'enable', + 'handoff-roaming': 'enable', + 'handoff-rssi': '13', + 'handoff-sta-thresh': '14', + 'ip-fragment-preventing': 'tcp-mss-adjust', + 'led-state': 'enable', + 'lldp': 'enable', + 'login-passwd': 'test_value_18', + 'login-passwd-change': 'yes', + 'max-clients': '20', + 'name': 'default_name_21', + 'poe-mode': 'auto', + 'split-tunneling-acl-local-ap-subnet': 'enable', + 'split-tunneling-acl-path': 'tunnel', + 'tun-mtu-downlink': '25', + 'tun-mtu-uplink': '26', + 'wan-port-mode': 'wan-lan' + } + + set_method_mock.assert_called_with('wireless-controller', 'wtp-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_wireless_controller_wtp_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', + 'wireless_controller_wtp_profile': { + 'random_attribute_not_valid': 'tag', + 'allowaccess': 'telnet', + 'ap_country': 'NA', + 'ble_profile': 'test_value_5', + 'comment': 'Comment.', + 'control_message_offload': 'ebp-frame', + 'dtls_in_kernel': 'enable', + 'dtls_policy': 'clear-text', + 'energy_efficient_ethernet': 'enable', + 'ext_info_enable': 'enable', + 'handoff_roaming': 'enable', + 'handoff_rssi': '13', + 'handoff_sta_thresh': '14', + 'ip_fragment_preventing': 'tcp-mss-adjust', + 'led_state': 'enable', + 'lldp': 'enable', + 'login_passwd': 'test_value_18', + 'login_passwd_change': 'yes', + 'max_clients': '20', + 'name': 'default_name_21', + 'poe_mode': 'auto', + 'split_tunneling_acl_local_ap_subnet': 'enable', + 'split_tunneling_acl_path': 'tunnel', + 'tun_mtu_downlink': '25', + 'tun_mtu_uplink': '26', + 'wan_port_mode': 'wan-lan' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_wireless_controller_wtp_profile.fortios_wireless_controller(input_data, fos_instance) + + expected_data = { + 'allowaccess': 'telnet', + 'ap-country': 'NA', + 'ble-profile': 'test_value_5', + 'comment': 'Comment.', + 'control-message-offload': 'ebp-frame', + 'dtls-in-kernel': 'enable', + 'dtls-policy': 'clear-text', + 'energy-efficient-ethernet': 'enable', + 'ext-info-enable': 'enable', + 'handoff-roaming': 'enable', + 'handoff-rssi': '13', + 'handoff-sta-thresh': '14', + 'ip-fragment-preventing': 'tcp-mss-adjust', + 'led-state': 'enable', + 'lldp': 'enable', + 'login-passwd': 'test_value_18', + 'login-passwd-change': 'yes', + 'max-clients': '20', + 'name': 'default_name_21', + 'poe-mode': 'auto', + 'split-tunneling-acl-local-ap-subnet': 'enable', + 'split-tunneling-acl-path': 'tunnel', + 'tun-mtu-downlink': '25', + 'tun-mtu-uplink': '26', + 'wan-port-mode': 'wan-lan' + } + + set_method_mock.assert_called_with('wireless-controller', 'wtp-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