From e585e948fb019ac02f6b4f268607d191d65dff40 Mon Sep 17 00:00:00 2001 From: ftntcorecse <43451990+ftntcorecse@users.noreply.github.com> Date: Mon, 4 Mar 2019 06:13:05 -0500 Subject: [PATCH] New FortiManager Module: fmgr_fwobj_service (#53027) * Auto Commit for: fmgr_fwobj_service * Auto Commit for: fmgr_fwobj_service --- .../fortimanager/fmgr_fwobj_service.py | 624 +++++++++++++++ .../fixtures/test_fmgr_fwobj_service.json | 745 ++++++++++++++++++ .../fortimanager/test_fmgr_fwobj_service.py | 123 +++ 3 files changed, 1492 insertions(+) create mode 100644 lib/ansible/modules/network/fortimanager/fmgr_fwobj_service.py create mode 100644 test/units/modules/network/fortimanager/fixtures/test_fmgr_fwobj_service.json create mode 100644 test/units/modules/network/fortimanager/test_fmgr_fwobj_service.py diff --git a/lib/ansible/modules/network/fortimanager/fmgr_fwobj_service.py b/lib/ansible/modules/network/fortimanager/fmgr_fwobj_service.py new file mode 100644 index 00000000000..9b9302cc2e5 --- /dev/null +++ b/lib/ansible/modules/network/fortimanager/fmgr_fwobj_service.py @@ -0,0 +1,624 @@ +#!/usr/bin/python +# +# This file is part of Ansible +# +# Ansible 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. +# +# Ansible 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 . +# + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community" +} + +DOCUMENTATION = ''' +--- +module: fmgr_fwobj_service +version_added: "2.8" +notes: + - Full Documentation at U(https://ftnt-ansible-docs.readthedocs.io/en/latest/). +author: + - Luke Weighall (@lweighall) + - Andrew Welsh (@Ghilli3) + - Jim Huber (@p4r4n0y1ng) +short_description: Manages FortiManager Firewall Service Objects. +description: + - Manages FortiManager Firewall Service Objects. + +options: + adom: + description: + -The ADOM the configuration should belong to. + required: false + default: root + + app_category: + description: + - Application category ID. + required: false + + app_service_type: + description: + - Application service type. + required: false + + application: + description: + - Application ID. + required: false + + category: + description: + - Service category. + required: false + + check_reset_range: + description: + - Enable disable RST check. + required: false + + color: + description: + - GUI icon color. + required: false + default: 22 + + comment: + description: + - Comment. + required: false + + custom_type: + description: + - Tells module what kind of custom service to be added. + choices: ['tcp_udp_sctp', 'icmp', 'icmp6', 'ip', 'http', 'ftp', 'connect', 'socks_tcp', 'socks_udp', 'all'] + default: all + required: false + + explicit_proxy: + description: + - Enable/disable explicit web proxy service. + choices: ['enable', 'disable'] + default: 'disable' + required: false + + fqdn: + description: + - Fully qualified domain name. + required: false + default: "" + + group_name: + description: + - Name of the Service Group. + required: false + + group_member: + description: + - Comma-Seperated list of members' names. + required: false + + icmp_code: + description: + - ICMP code. + required: false + + icmp_type: + description: + - ICMP type. + required: false + + iprange: + description: + - Start IP-End IP. + required: false + default: "0.0.0.0" + + name: + description: + - Custom service name. + required: false + + mode: + description: + - Sets one of three modes for managing the object. + choices: ['add', 'set', 'delete'] + default: add + required: false + + object_type: + description: + - Tells module if we are adding a custom service, category, or group. + choices: ['custom', 'group', 'category'] + required: false + + protocol: + description: + - Protocol type. + required: false + + protocol_number: + description: + - IP protocol number. + required: false + + sctp_portrange: + description: + - Multiple SCTP port ranges. Comma separated list of destination ports to add (i.e. '443,80'). + - Syntax is + - If no sourcePort is defined, it assumes all of them. + - Ranges can be defined with a hyphen - + - Examples -- '443' (destPort 443 only) '443:1000-2000' (destPort 443 from source ports 1000-2000). + - String multiple together in same quotes, comma separated. ('443:1000-2000, 80:1000-2000'). + required: false + + session_ttl: + description: + - Session TTL (300 - 604800, 0 = default). + required: false + default: 0 + + tcp_halfclose_timer: + description: + - TCP half close timeout (1 - 86400 sec, 0 = default). + required: false + default: 0 + + tcp_halfopen_timer: + description: + - TCP half close timeout (1 - 86400 sec, 0 = default). + required: false + default: 0 + + tcp_portrange: + description: + - Comma separated list of destination ports to add (i.e. '443,80'). + - Syntax is + - If no sourcePort is defined, it assumes all of them. + - Ranges can be defined with a hyphen - + - Examples -- '443' (destPort 443 only) '443:1000-2000' (destPort 443 from source ports 1000-2000). + - String multiple together in same quotes, comma separated. ('443:1000-2000, 80:1000-2000'). + required: false + + tcp_timewait_timer: + description: + - TCP half close timeout (1 - 300 sec, 0 = default). + required: false + default: 0 + + udp_idle_timer: + description: + - TCP half close timeout (0 - 86400 sec, 0 = default). + required: false + default: 0 + + udp_portrange: + description: + - Comma separated list of destination ports to add (i.e. '443,80'). + - Syntax is + - If no sourcePort is defined, it assumes all of them. + - Ranges can be defined with a hyphen - + - Examples -- '443' (destPort 443 only) '443:1000-2000' (destPort 443 from source ports 1000-2000). + - String multiple together in same quotes, comma separated. ('443:1000-2000, 80:1000-2000'). + required: false + + visibility: + description: + - Enable/disable service visibility. + required: false + choices: ["enable", "disable"] + default: "enable" + +''' + +EXAMPLES = ''' +- name: ADD A CUSTOM SERVICE FOR TCP/UDP/SCP + fmgr_fwobj_service: + adom: "ansible" + name: "ansible_custom_service" + object_type: "custom" + custom_type: "tcp_udp_sctp" + tcp_portrange: "443" + udp_portrange: "51" + sctp_portrange: "100" + +- name: ADD A CUSTOM SERVICE FOR TCP/UDP/SCP WITH SOURCE RANGES AND MULTIPLES + fmgr_fwobj_service: + adom: "ansible" + name: "ansible_custom_serviceWithSource" + object_type: "custom" + custom_type: "tcp_udp_sctp" + tcp_portrange: "443:2000-1000,80-82:10000-20000" + udp_portrange: "51:100-200,162:200-400" + sctp_portrange: "100:2000-2500" + +- name: ADD A CUSTOM SERVICE FOR ICMP + fmgr_fwobj_service: + adom: "ansible" + name: "ansible_custom_icmp" + object_type: "custom" + custom_type: "icmp" + icmp_type: "8" + icmp_code: "3" + +- name: ADD A CUSTOM SERVICE FOR ICMP6 + fmgr_fwobj_service: + adom: "ansible" + name: "ansible_custom_icmp6" + object_type: "custom" + custom_type: "icmp6" + icmp_type: "5" + icmp_code: "1" + +- name: ADD A CUSTOM SERVICE FOR IP - GRE + fmgr_fwobj_service: + adom: "ansible" + name: "ansible_custom_icmp6" + object_type: "custom" + custom_type: "ip" + protocol_number: "47" + +- name: ADD A CUSTOM PROXY FOR ALL WITH SOURCE RANGES AND MULTIPLES + fmgr_fwobj_service: + adom: "ansible" + name: "ansible_custom_proxy_all" + object_type: "custom" + custom_type: "all" + explicit_proxy: "enable" + tcp_portrange: "443:2000-1000,80-82:10000-20000" + iprange: "www.ansible.com" +''' + +RETURN = """ +api_result: + description: full API response, includes status code and message + returned: always + type: str +""" + +from ansible.module_utils.basic import AnsibleModule, env_fallback +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortimanager.fortimanager import FortiManagerHandler +from ansible.module_utils.network.fortimanager.common import FMGBaseException +from ansible.module_utils.network.fortimanager.common import FMGRCommon +from ansible.module_utils.network.fortimanager.common import DEFAULT_RESULT_OBJ +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG +from ansible.module_utils.network.fortimanager.common import scrub_dict + + +def fmgr_fwobj_service_custom(fmgr, paramgram): + """ + description: + - the tcp and udp-portrange parameters are in a list when there are multiple. they are not in a list when they + singular or by themselves (only 1 was listed) + - the syntax for this is (destPort:sourcePort). Ranges are (xxxx-xxxx) i.e. 443:443, or 443:1000-2000. + - if you leave out the second field after the colon (source port) it assumes any source port (which is usual) + - multiples would look like ['443:1000-2000','80'] + - a single would look simple like "443:1000-2000" without the list around it ( a string!) + - the protocol parameter is the protocol NUMBER, not the string of it. + :param fmgr: The fmgr object instance from fortimanager.py + :type fmgr: class object + :param paramgram: The formatted dictionary of options to process + :type paramgram: dict + :return: The response from the FortiManager + :rtype: dict + """ + response = DEFAULT_RESULT_OBJ + if paramgram["mode"] in ['set', 'add']: + # SET THE URL FOR ADD / SET + url = '/pm/config/adom/{adom}/obj/firewall/service/custom'.format(adom=paramgram["adom"]) + # BUILD THE DEFAULT DATAGRAM + datagram = { + # ADVANCED OPTIONS + "app-category": paramgram["app-category"], + "app-service-type": paramgram["app-service-type"], + "application": paramgram["application"], + "category": paramgram["category"], + "check-reset-range": paramgram["check-reset-range"], + "color": paramgram["color"], + "session-ttl": paramgram["session-ttl"], + "tcp-halfclose-timer": paramgram["tcp-halfclose-timer"], + "tcp-halfopen-timer": paramgram["tcp-halfopen-timer"], + "tcp-timewait-timer": paramgram["tcp-timewait-timer"], + "udp-idle-timer": paramgram["udp-idle-timer"], + "visibility": paramgram["visibility"], + "comment": paramgram["comment"], + "proxy": paramgram["explicit-proxy"], + "name": paramgram["name"] + } + + if datagram["proxy"] == "disable": + ####################################### + # object-type = "TCP/UDP/SCTP" + ####################################### + if paramgram["custom_type"] == "tcp_udp_sctp": + datagram["protocol"] = "TCP/UDP/SCTP" + # PROCESS PORT RANGES TO PUT INTO THE PROPER SYNTAX + if paramgram["tcp-portrange"] is not None: + tcp_list = [] + for tcp in paramgram["tcp-portrange"].split(","): + tcp = tcp.strip() + tcp_list.append(tcp) + datagram["tcp-portrange"] = tcp_list + + if paramgram["udp-portrange"] is not None: + udp_list = [] + for udp in paramgram["udp-portrange"].split(","): + udp = udp.strip() + udp_list.append(udp) + datagram["udp-portrange"] = udp_list + + if paramgram["sctp-portrange"] is not None: + sctp_list = [] + for sctp in paramgram["sctp-portrange"].split(","): + sctp = sctp.strip() + sctp_list.append(sctp) + datagram["sctp-portrange"] = sctp_list + + ####################################### + # object-type = "ICMP" + ####################################### + if paramgram["custom_type"] == "icmp": + datagram["icmpcode"] = paramgram["icmp_code"] + datagram["icmptype"] = paramgram["icmp_type"] + datagram["protocol"] = "ICMP" + + ####################################### + # object-type = "ICMP6" + ####################################### + if paramgram["custom_type"] == "icmp6": + datagram["icmpcode"] = paramgram["icmp_code"] + datagram["icmptype"] = paramgram["icmp_type"] + datagram["protocol"] = "ICMP6" + + ####################################### + # object-type = "IP" + ####################################### + if paramgram["custom_type"] == "ip": + datagram["protocol"] = "IP" + datagram["protocol-number"] = paramgram["protocol-number"] + + ####################################### + # object-type in any of the explicit proxy options + ####################################### + if datagram["proxy"] == "enable": + datagram["protocol"] = paramgram["custom_type"].upper() + datagram["iprange"] = paramgram["iprange"] + + # PROCESS PROXY TCP PORT RANGES TO PUT INTO THE PROPER SYNTAX + if paramgram["tcp-portrange"] is not None: + tcp_list = [] + for tcp in paramgram["tcp-portrange"].split(","): + tcp = tcp.strip() + tcp_list.append(tcp) + datagram["tcp-portrange"] = tcp_list + + if paramgram["mode"] == "delete": + datagram = { + "name": paramgram["name"] + } + # SET DELETE URL + url = '/pm/config/adom/{adom}/obj/firewall/service/custom' \ + '/{name}'.format(adom=paramgram["adom"], name=paramgram["name"]) + + datagram = scrub_dict(datagram) + response = fmgr.process_request(url, datagram, paramgram["mode"]) + return response + + +def fmgr_fwobj_service_group(fmgr, paramgram): + """ + :param fmgr: The fmgr object instance from fortimanager.py + :type fmgr: class object + :param paramgram: The formatted dictionary of options to process + :type paramgram: dict + :return: The response from the FortiManager + :rtype: dict + """ + response = DEFAULT_RESULT_OBJ + if paramgram["mode"] in ['set', 'add']: + url = '/pm/config/adom/{adom}/obj/firewall/service/group'.format(adom=paramgram["adom"]) + datagram = { + "name": paramgram["group-name"], + "comment": paramgram["comment"], + "proxy": paramgram["explicit-proxy"], + "color": paramgram["color"] + } + + members = paramgram["group-member"] + member = [] + for obj in members.split(","): + member.append(obj.strip()) + datagram["member"] = member + + if paramgram["mode"] == "delete": + datagram = { + "name": paramgram["name"] + } + # SET DELETE URL + url = '/pm/config/adom/{adom}/obj/firewall/service/group' \ + '/{name}'.format(adom=paramgram["adom"], name=paramgram["group-name"]) + + datagram = scrub_dict(datagram) + response = fmgr.process_request(url, datagram, paramgram["mode"]) + return response + + +def fmgr_fwobj_service_category(fmgr, paramgram): + """ + :param fmgr: The fmgr object instance from fortimanager.py + :type fmgr: class object + :param paramgram: The formatted dictionary of options to process + :type paramgram: dict + :return: The response from the FortiManager + :rtype: dict + """ + response = DEFAULT_RESULT_OBJ + if paramgram["mode"] in ['set', 'add']: + url = '/pm/config/adom/{adom}/obj/firewall/service/category'.format(adom=paramgram["adom"]) + # GET RID OF ANY WHITESPACE + category = paramgram["category"] + category = category.strip() + + datagram = { + "name": paramgram["category"], + "comment": "Created by Ansible" + } + + # IF MODE = DELETE + if paramgram["mode"] == "delete": + datagram = { + "name": paramgram["name"] + } + # SET DELETE URL + url = '/pm/config/adom/{adom}/obj/firewall/service/category' \ + '/{name}'.format(adom=paramgram["adom"], name=paramgram["category"]) + + datagram = scrub_dict(datagram) + response = fmgr.process_request(url, datagram, paramgram["mode"]) + return response + + +def main(): + argument_spec = dict( + adom=dict(required=False, type="str", default="root"), + mode=dict(required=False, type="str", choices=['add', 'set', 'delete'], default="add"), + app_category=dict(required=False, type="str"), + app_service_type=dict(required=False, type="str"), + application=dict(required=False, type="str"), + category=dict(required=False, type="str"), + check_reset_range=dict(required=False, type="str"), + color=dict(required=False, type="int", default=22), + comment=dict(required=False, type="str"), + custom_type=dict(required=False, type="str", choices=['tcp_udp_sctp', 'icmp', 'icmp6', 'ip', 'http', 'ftp', + 'connect', 'socks_tcp', 'socks_udp', 'all'], + default="all"), + explicit_proxy=dict(required=False, type="str", choices=['enable', 'disable'], default="disable"), + fqdn=dict(required=False, type="str", default=""), + group_name=dict(required=False, type="str"), + group_member=dict(required=False, type="str"), + icmp_code=dict(required=False, type="int"), + icmp_type=dict(required=False, type="int"), + iprange=dict(required=False, type="str", default="0.0.0.0"), + name=dict(required=False, type="str"), + protocol=dict(required=False, type="str"), + protocol_number=dict(required=False, type="int"), + sctp_portrange=dict(required=False, type="str"), + session_ttl=dict(required=False, type="int", default=0), + object_type=dict(required=False, type="str", choices=['custom', 'group', 'category']), + tcp_halfclose_timer=dict(required=False, type="int", default=0), + tcp_halfopen_timer=dict(required=False, type="int", default=0), + tcp_portrange=dict(required=False, type="str"), + tcp_timewait_timer=dict(required=False, type="int", default=0), + udp_idle_timer=dict(required=False, type="int", default=0), + udp_portrange=dict(required=False, type="str"), + visibility=dict(required=False, type="str", default="enable", choices=["enable", "disable"]), + + ) + + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) + # MODULE DATAGRAM + paramgram = { + "adom": module.params["adom"], + "app-category": module.params["app_category"], + "app-service-type": module.params["app_service_type"], + "application": module.params["application"], + "category": module.params["category"], + "check-reset-range": module.params["check_reset_range"], + "color": module.params["color"], + "comment": module.params["comment"], + "custom_type": module.params["custom_type"], + "explicit-proxy": module.params["explicit_proxy"], + "fqdn": module.params["fqdn"], + "group-name": module.params["group_name"], + "group-member": module.params["group_member"], + "icmp_code": module.params["icmp_code"], + "icmp_type": module.params["icmp_type"], + "iprange": module.params["iprange"], + "name": module.params["name"], + "mode": module.params["mode"], + "protocol": module.params["protocol"], + "protocol-number": module.params["protocol_number"], + "sctp-portrange": module.params["sctp_portrange"], + "object_type": module.params["object_type"], + "session-ttl": module.params["session_ttl"], + "tcp-halfclose-timer": module.params["tcp_halfclose_timer"], + "tcp-halfopen-timer": module.params["tcp_halfopen_timer"], + "tcp-portrange": module.params["tcp_portrange"], + "tcp-timewait-timer": module.params["tcp_timewait_timer"], + "udp-idle-timer": module.params["udp_idle_timer"], + "udp-portrange": module.params["udp_portrange"], + "visibility": module.params["visibility"], + } + module.paramgram = paramgram + fmgr = None + if module._socket_path: + connection = Connection(module._socket_path) + fmgr = FortiManagerHandler(connection, module) + fmgr.tools = FMGRCommon() + else: + module.fail_json(**FAIL_SOCKET_MSG) + + results = DEFAULT_RESULT_OBJ + + try: + # CHECK FOR CATEGORIES TO ADD + # THIS IS ONLY WHEN OBJECT_TYPE ISN'T SPECIFICALLY ADDING A CATEGORY! + # WE NEED TO ADD THE CATEGORY BEFORE ADDING THE OBJECT + # IF ANY category ARE DEFINED AND MODE IS ADD OR SET LETS ADD THOSE + # THIS IS A "BLIND ADD" AND THE EXIT CODE FOR OBJECT ALREADY EXISTS IS TREATED AS A PASS + if paramgram["category"] is not None and paramgram["mode"] in ['add', 'set'] \ + and paramgram["object_type"] != "category": + category_add = fmgr_fwobj_service_category(fmgr, paramgram) + fmgr.govern_response(module=module, results=category_add, + ansible_facts=fmgr.construct_ansible_facts(category_add, module.params, paramgram)) + except Exception as err: + raise FMGBaseException(err) + + try: + # IF OBJECT_TYPE IS CATEGORY... + if paramgram["object_type"] == 'category': + results = fmgr_fwobj_service_category(fmgr, paramgram) + fmgr.govern_response(module=module, results=results, good_codes=[0, -2, -3], + ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) + except Exception as err: + raise FMGBaseException(err) + + try: + # IF OBJECT_TYPE IS CUSTOM... + if paramgram["object_type"] == 'custom': + results = fmgr_fwobj_service_custom(fmgr, paramgram) + fmgr.govern_response(module=module, results=results, good_codes=[0, -2, -3], + ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) + except Exception as err: + raise FMGBaseException(err) + + try: + # IF OBJECT_TYPE IS GROUP... + if paramgram["object_type"] == 'group': + results = fmgr_fwobj_service_group(fmgr, paramgram) + fmgr.govern_response(module=module, results=results, good_codes=[0, -2, -3], + ansible_facts=fmgr.construct_ansible_facts(results, module.params, paramgram)) + except Exception as err: + raise FMGBaseException(err) + + return module.exit_json(**results[1]) + + +if __name__ == "__main__": + main() diff --git a/test/units/modules/network/fortimanager/fixtures/test_fmgr_fwobj_service.json b/test/units/modules/network/fortimanager/fixtures/test_fmgr_fwobj_service.json new file mode 100644 index 00000000000..7909db218a1 --- /dev/null +++ b/test/units/modules/network/fortimanager/fixtures/test_fmgr_fwobj_service.json @@ -0,0 +1,745 @@ +{ + "fmgr_fwobj_service_custom": [ + { + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/custom/ansible_custom_service" + }, + "datagram_sent": { + "name": "ansible_custom_service" + }, + "paramgram_used": { + "comment": null, + "protocol": null, + "custom_type": "all", + "color": 22, + "object_type": "custom", + "group-name": null, + "tcp-halfclose-timer": 0, + "icmp_type": null, + "iprange": "0.0.0.0", + "category": null, + "protocol-number": null, + "udp-idle-timer": 0, + "explicit-proxy": "disable", + "group-member": null, + "application": null, + "tcp-portrange": null, + "icmp_code": null, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": "ansible_custom_service", + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "delete", + "tcp-halfopen-timer": 0, + "udp-portrange": null, + "sctp-portrange": null + }, + "post_method": "delete" + }, + { + "paramgram_used": { + "comment": null, + "protocol-number": null, + "protocol": null, + "custom_type": "all", + "color": 22, + "object_type": "custom", + "group-name": null, + "tcp-halfclose-timer": 0, + "icmp_type": null, + "iprange": "0.0.0.0", + "category": null, + "explicit-proxy": "disable", + "udp-idle-timer": 0, + "group-member": null, + "application": null, + "tcp-portrange": null, + "icmp_code": null, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": "ansible_custom_icmp", + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "delete", + "tcp-halfopen-timer": 0, + "udp-portrange": null, + "sctp-portrange": null + }, + "datagram_sent": { + "name": "ansible_custom_icmp" + }, + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/custom/ansible_custom_icmp" + }, + "post_method": "delete" + }, + { + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/custom/ansible_custom_icmp6" + }, + "datagram_sent": { + "name": "ansible_custom_icmp6" + }, + "paramgram_used": { + "comment": null, + "protocol": null, + "custom_type": "all", + "color": 22, + "object_type": "custom", + "group-name": null, + "tcp-halfclose-timer": 0, + "icmp_type": null, + "iprange": "0.0.0.0", + "category": null, + "protocol-number": null, + "udp-idle-timer": 0, + "explicit-proxy": "disable", + "group-member": null, + "application": null, + "tcp-portrange": null, + "icmp_code": null, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": "ansible_custom_icmp6", + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "delete", + "tcp-halfopen-timer": 0, + "udp-portrange": null, + "sctp-portrange": null + }, + "post_method": "delete" + }, + { + "paramgram_used": { + "comment": null, + "protocol-number": null, + "protocol": null, + "custom_type": "all", + "color": 22, + "object_type": "custom", + "group-name": null, + "tcp-halfclose-timer": 0, + "icmp_type": null, + "iprange": "0.0.0.0", + "category": null, + "explicit-proxy": "disable", + "udp-idle-timer": 0, + "group-member": null, + "application": null, + "tcp-portrange": null, + "icmp_code": null, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": "ansible_custom_ip", + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "delete", + "tcp-halfopen-timer": 0, + "udp-portrange": null, + "sctp-portrange": null + }, + "datagram_sent": { + "name": "ansible_custom_ip" + }, + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/custom/ansible_custom_ip" + }, + "post_method": "delete" + }, + { + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/custom/ansible_custom_serviceWithSource" + }, + "datagram_sent": { + "name": "ansible_custom_serviceWithSource" + }, + "paramgram_used": { + "comment": null, + "protocol": null, + "custom_type": "all", + "color": 22, + "object_type": "custom", + "group-name": null, + "tcp-halfclose-timer": 0, + "icmp_type": null, + "iprange": "0.0.0.0", + "category": null, + "protocol-number": null, + "udp-idle-timer": 0, + "explicit-proxy": "disable", + "group-member": null, + "application": null, + "tcp-portrange": null, + "icmp_code": null, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": "ansible_custom_serviceWithSource", + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "delete", + "tcp-halfopen-timer": 0, + "udp-portrange": null, + "sctp-portrange": null + }, + "post_method": "delete" + }, + { + "paramgram_used": { + "comment": null, + "protocol-number": null, + "protocol": null, + "custom_type": "all", + "color": 22, + "object_type": "custom", + "group-name": null, + "tcp-halfclose-timer": 0, + "icmp_type": null, + "iprange": "0.0.0.0", + "category": null, + "explicit-proxy": "disable", + "udp-idle-timer": 0, + "group-member": null, + "application": null, + "tcp-portrange": null, + "icmp_code": null, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": "ansible_custom_proxy_all", + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "delete", + "tcp-halfopen-timer": 0, + "udp-portrange": null, + "sctp-portrange": null + }, + "datagram_sent": { + "name": "ansible_custom_proxy_all" + }, + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/custom/ansible_custom_proxy_all" + }, + "post_method": "delete" + }, + { + "paramgram_used": { + "comment": null, + "protocol-number": null, + "protocol": null, + "custom_type": "tcp_udp_sctp", + "color": 22, + "object_type": "custom", + "group-name": null, + "tcp-halfclose-timer": 0, + "icmp_type": null, + "iprange": "0.0.0.0", + "category": "ansibleCategoryTest", + "explicit-proxy": "disable", + "udp-idle-timer": 0, + "group-member": null, + "application": null, + "tcp-portrange": "443", + "icmp_code": null, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": "ansible_custom_service", + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "add", + "tcp-halfopen-timer": 0, + "udp-portrange": "51", + "sctp-portrange": "100" + }, + "datagram_sent": { + "category": "ansibleCategoryTest", + "protocol": "TCP/UDP/SCTP", + "name": "ansible_custom_service", + "color": 22, + "visibility": "enable", + "proxy": "disable", + "sctp-portrange": [ + "100" + ], + "udp-portrange": [ + "51" + ], + "tcp-portrange": [ + "443" + ] + }, + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/custom" + }, + "post_method": "add" + }, + { + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/custom" + }, + "datagram_sent": { + "protocol": "TCP/UDP/SCTP", + "name": "ansible_custom_serviceWithSource", + "color": 22, + "visibility": "enable", + "proxy": "disable", + "tcp-portrange": [ + "443:1000-2000", + "80-82:10000-20000" + ], + "udp-portrange": [ + "51:100-200", + "162:200-400" + ], + "sctp-portrange": [ + "100:2000-2500" + ] + }, + "paramgram_used": { + "comment": null, + "protocol": null, + "custom_type": "tcp_udp_sctp", + "color": 22, + "object_type": "custom", + "group-name": null, + "tcp-halfclose-timer": 0, + "icmp_type": null, + "iprange": "0.0.0.0", + "category": null, + "protocol-number": null, + "udp-idle-timer": 0, + "explicit-proxy": "disable", + "group-member": null, + "application": null, + "tcp-portrange": "443:1000-2000,80-82:10000-20000", + "icmp_code": null, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": "ansible_custom_serviceWithSource", + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "add", + "tcp-halfopen-timer": 0, + "udp-portrange": "51:100-200,162:200-400", + "sctp-portrange": "100:2000-2500" + }, + "post_method": "add" + }, + { + "paramgram_used": { + "comment": null, + "protocol-number": null, + "protocol": null, + "custom_type": "icmp", + "color": 22, + "object_type": "custom", + "group-name": null, + "tcp-halfclose-timer": 0, + "icmp_type": 8, + "iprange": "0.0.0.0", + "category": null, + "explicit-proxy": "disable", + "udp-idle-timer": 0, + "group-member": null, + "application": null, + "tcp-portrange": null, + "icmp_code": 3, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": "ansible_custom_icmp", + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "add", + "tcp-halfopen-timer": 0, + "udp-portrange": null, + "sctp-portrange": null + }, + "datagram_sent": { + "protocol": "ICMP", + "name": "ansible_custom_icmp", + "color": 22, + "visibility": "enable", + "icmptype": 8, + "proxy": "disable", + "icmpcode": 3 + }, + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/custom" + }, + "post_method": "add" + }, + { + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/custom" + }, + "datagram_sent": { + "protocol": "ICMP6", + "name": "ansible_custom_icmp6", + "color": 22, + "visibility": "enable", + "icmptype": 5, + "proxy": "disable", + "icmpcode": 1 + }, + "paramgram_used": { + "comment": null, + "protocol": null, + "custom_type": "icmp6", + "color": 22, + "object_type": "custom", + "group-name": null, + "tcp-halfclose-timer": 0, + "icmp_type": 5, + "iprange": "0.0.0.0", + "category": null, + "protocol-number": null, + "udp-idle-timer": 0, + "explicit-proxy": "disable", + "group-member": null, + "application": null, + "tcp-portrange": null, + "icmp_code": 1, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": "ansible_custom_icmp6", + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "add", + "tcp-halfopen-timer": 0, + "udp-portrange": null, + "sctp-portrange": null + }, + "post_method": "add" + }, + { + "paramgram_used": { + "comment": null, + "protocol-number": 12, + "protocol": null, + "custom_type": "ip", + "color": 22, + "object_type": "custom", + "group-name": null, + "tcp-halfclose-timer": 0, + "icmp_type": null, + "iprange": "0.0.0.0", + "category": null, + "explicit-proxy": "disable", + "udp-idle-timer": 0, + "group-member": null, + "application": null, + "tcp-portrange": null, + "icmp_code": null, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": "ansible_custom_ip", + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "add", + "tcp-halfopen-timer": 0, + "udp-portrange": null, + "sctp-portrange": null + }, + "datagram_sent": { + "protocol-number": 12, + "protocol": "IP", + "name": "ansible_custom_ip", + "color": 22, + "visibility": "enable", + "proxy": "disable" + }, + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/custom" + }, + "post_method": "add" + }, + { + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/custom" + }, + "datagram_sent": { + "protocol": "ALL", + "name": "ansible_custom_proxy_all", + "color": 22, + "visibility": "enable", + "proxy": "enable", + "iprange": "www.ansible.com", + "tcp-portrange": [ + "443:1000-2000", + "80-82:10000-20000" + ] + }, + "paramgram_used": { + "comment": null, + "protocol": null, + "custom_type": "all", + "color": 22, + "object_type": "custom", + "group-name": null, + "tcp-halfclose-timer": 0, + "icmp_type": null, + "iprange": "www.ansible.com", + "category": null, + "protocol-number": null, + "udp-idle-timer": 0, + "explicit-proxy": "enable", + "group-member": null, + "application": null, + "tcp-portrange": "443:1000-2000,80-82:10000-20000", + "icmp_code": null, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": "ansible_custom_proxy_all", + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "add", + "tcp-halfopen-timer": 0, + "udp-portrange": null, + "sctp-portrange": null + }, + "post_method": "add" + } + ], + "fmgr_fwobj_service_group": [ + { + "raw_response": { + "status": { + "message": "Object does not exist", + "code": -3 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/group/ansibleTestGroup" + }, + "datagram_sent": {}, + "paramgram_used": { + "comment": null, + "protocol": null, + "custom_type": "all", + "color": 22, + "object_type": "group", + "group-name": "ansibleTestGroup", + "tcp-halfclose-timer": 0, + "icmp_type": null, + "iprange": "0.0.0.0", + "category": null, + "protocol-number": null, + "udp-idle-timer": 0, + "explicit-proxy": "disable", + "group-member": null, + "application": null, + "tcp-portrange": null, + "icmp_code": null, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": null, + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "delete", + "tcp-halfopen-timer": 0, + "udp-portrange": null, + "sctp-portrange": null + }, + "post_method": "delete" + }, + { + "paramgram_used": { + "comment": "created by ansible", + "protocol-number": null, + "protocol": null, + "custom_type": "all", + "color": 10, + "object_type": "group", + "group-name": "ansibleTestGroup", + "tcp-halfclose-timer": 0, + "icmp_type": null, + "iprange": "0.0.0.0", + "category": null, + "explicit-proxy": "disable", + "udp-idle-timer": 0, + "group-member": "ansible_custom_ip, ansible_custom_icmp, ansible_custom_service", + "application": null, + "tcp-portrange": null, + "icmp_code": null, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": null, + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "add", + "tcp-halfopen-timer": 0, + "udp-portrange": null, + "sctp-portrange": null + }, + "datagram_sent": { + "comment": "created by ansible", + "color": 10, + "member": [ + "ansible_custom_ip", + "ansible_custom_icmp", + "ansible_custom_service" + ], + "name": "ansibleTestGroup", + "proxy": "disable" + }, + "raw_response": { + "status": { + "message": "datasrc invalid. object: firewall service group member ansibleTestGroup. detail: ansible_custom_ip. solution: data not exist", + "code": -10131 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/group" + }, + "post_method": "add" + } + ], + "fmgr_fwobj_service_category": [ + { + "raw_response": { + "status": { + "message": "Object already exists", + "code": -2 + }, + "url": "/pm/config/adom/ansible/obj/firewall/service/category" + }, + "datagram_sent": { + "comment": "Created by Ansible", + "name": "ansibleCategoryTest" + }, + "paramgram_used": { + "comment": null, + "protocol": null, + "custom_type": "tcp_udp_sctp", + "color": 22, + "object_type": "custom", + "group-name": null, + "tcp-halfclose-timer": 0, + "icmp_type": null, + "iprange": "0.0.0.0", + "category": "ansibleCategoryTest", + "protocol-number": null, + "udp-idle-timer": 0, + "explicit-proxy": "disable", + "group-member": null, + "application": null, + "tcp-portrange": "443", + "icmp_code": null, + "session-ttl": 0, + "adom": "ansible", + "visibility": "enable", + "tcp-timewait-timer": 0, + "name": "ansible_custom_service", + "app-service-type": null, + "fqdn": "", + "app-category": null, + "check-reset-range": null, + "mode": "add", + "tcp-halfopen-timer": 0, + "udp-portrange": "51", + "sctp-portrange": "100" + }, + "post_method": "add" + } + ] +} diff --git a/test/units/modules/network/fortimanager/test_fmgr_fwobj_service.py b/test/units/modules/network/fortimanager/test_fmgr_fwobj_service.py new file mode 100644 index 00000000000..85bfcc2f539 --- /dev/null +++ b/test/units/modules/network/fortimanager/test_fmgr_fwobj_service.py @@ -0,0 +1,123 @@ +# Copyright 2018 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 +from ansible.module_utils.network.fortimanager.fortimanager import FortiManagerHandler +import pytest + +try: + from ansible.modules.network.fortimanager import fmgr_fwobj_service +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +def load_fixtures(): + fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures') + "/{filename}.json".format( + filename=os.path.splitext(os.path.basename(__file__))[0]) + try: + with open(fixture_path, "r") as fixture_file: + fixture_data = json.load(fixture_file) + except IOError: + return [] + return [fixture_data] + + +@pytest.fixture(autouse=True) +def module_mock(mocker): + connection_class_mock = mocker.patch('ansible.module_utils.basic.AnsibleModule') + return connection_class_mock + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortimanager.fmgr_fwobj_service.Connection') + return connection_class_mock + + +@pytest.fixture(scope="function", params=load_fixtures()) +def fixture_data(request): + func_name = request.function.__name__.replace("test_", "") + return request.param.get(func_name, None) + + +fmg_instance = FortiManagerHandler(connection_mock, module_mock) + + +def test_fmgr_fwobj_service_custom(fixture_data, mocker): + mocker.patch("ansible.module_utils.network.fortimanager.fortimanager.FortiManagerHandler.process_request", + side_effect=fixture_data) + + # Test using fixture 1 # + output = fmgr_fwobj_service.fmgr_fwobj_service_custom(fmg_instance, fixture_data[0]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + # Test using fixture 2 # + output = fmgr_fwobj_service.fmgr_fwobj_service_custom(fmg_instance, fixture_data[1]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + # Test using fixture 3 # + output = fmgr_fwobj_service.fmgr_fwobj_service_custom(fmg_instance, fixture_data[2]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + # Test using fixture 4 # + output = fmgr_fwobj_service.fmgr_fwobj_service_custom(fmg_instance, fixture_data[3]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + # Test using fixture 5 # + output = fmgr_fwobj_service.fmgr_fwobj_service_custom(fmg_instance, fixture_data[4]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + # Test using fixture 6 # + output = fmgr_fwobj_service.fmgr_fwobj_service_custom(fmg_instance, fixture_data[5]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + # Test using fixture 7 # + output = fmgr_fwobj_service.fmgr_fwobj_service_custom(fmg_instance, fixture_data[6]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + # Test using fixture 8 # + output = fmgr_fwobj_service.fmgr_fwobj_service_custom(fmg_instance, fixture_data[7]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + # Test using fixture 9 # + output = fmgr_fwobj_service.fmgr_fwobj_service_custom(fmg_instance, fixture_data[8]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + # Test using fixture 10 # + output = fmgr_fwobj_service.fmgr_fwobj_service_custom(fmg_instance, fixture_data[9]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + # Test using fixture 11 # + output = fmgr_fwobj_service.fmgr_fwobj_service_custom(fmg_instance, fixture_data[10]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + # Test using fixture 12 # + output = fmgr_fwobj_service.fmgr_fwobj_service_custom(fmg_instance, fixture_data[11]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + + +def test_fmgr_fwobj_service_group(fixture_data, mocker): + mocker.patch("ansible.module_utils.network.fortimanager.fortimanager.FortiManagerHandler.process_request", + side_effect=fixture_data) + + # Test using fixture 1 # + output = fmgr_fwobj_service.fmgr_fwobj_service_group(fmg_instance, fixture_data[0]['paramgram_used']) + assert output['raw_response']['status']['code'] == -3 + # Test using fixture 2 # + output = fmgr_fwobj_service.fmgr_fwobj_service_group(fmg_instance, fixture_data[1]['paramgram_used']) + assert output['raw_response']['status']['code'] == -10131 + + +def test_fmgr_fwobj_service_category(fixture_data, mocker): + mocker.patch("ansible.module_utils.network.fortimanager.fortimanager.FortiManagerHandler.process_request", + side_effect=fixture_data) + + # Test using fixture 1 # + output = fmgr_fwobj_service.fmgr_fwobj_service_category(fmg_instance, fixture_data[0]['paramgram_used']) + assert output['raw_response']['status']['code'] == -2