diff --git a/lib/ansible/modules/network/fortimanager/fmgr_secprof_appctrl.py b/lib/ansible/modules/network/fortimanager/fmgr_secprof_appctrl.py new file mode 100644 index 00000000000..0417858676d --- /dev/null +++ b/lib/ansible/modules/network/fortimanager/fmgr_secprof_appctrl.py @@ -0,0 +1,521 @@ +#!/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 = {'status': ['preview'], + 'supported_by': 'community', + 'metadata_version': '1.1'} + +DOCUMENTATION = ''' +--- +module: fmgr_secprof_appctrl +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: Manage application control security profiles +description: + - Manage application control security profiles within FortiManager + +options: + adom: + description: + - The ADOM the configuration should belong to. + required: false + default: root + + mode: + description: + - Sets one of three modes for managing the object. + - Allows use of soft-adds instead of overwriting existing values + choices: ['add', 'set', 'delete', 'update'] + required: false + default: add + + unknown_application_log: + description: + - Enable/disable logging for unknown applications. + - choice | disable | Disable logging for unknown applications. + - choice | enable | Enable logging for unknown applications. + required: false + choices: ["disable", "enable"] + + unknown_application_action: + description: + - Pass or block traffic from unknown applications. + - choice | pass | Pass or allow unknown applications. + - choice | block | Drop or block unknown applications. + required: false + choices: ["pass", "block"] + + replacemsg_group: + description: + - Replacement message group. + required: false + + p2p_black_list: + description: + - NO DESCRIPTION PARSED ENTER MANUALLY + - FLAG Based Options. Specify multiple in list form. + - flag | skype | Skype. + - flag | edonkey | Edonkey. + - flag | bittorrent | Bit torrent. + required: false + choices: ["skype", "edonkey", "bittorrent"] + + other_application_log: + description: + - Enable/disable logging for other applications. + - choice | disable | Disable logging for other applications. + - choice | enable | Enable logging for other applications. + required: false + choices: ["disable", "enable"] + + other_application_action: + description: + - Action for other applications. + - choice | pass | Allow sessions matching an application in this application list. + - choice | block | Block sessions matching an application in this application list. + required: false + choices: ["pass", "block"] + + options: + description: + - NO DESCRIPTION PARSED ENTER MANUALLY + - FLAG Based Options. Specify multiple in list form. + - flag | allow-dns | Allow DNS. + - flag | allow-icmp | Allow ICMP. + - flag | allow-http | Allow generic HTTP web browsing. + - flag | allow-ssl | Allow generic SSL communication. + - flag | allow-quic | Allow QUIC. + required: false + choices: ["allow-dns", "allow-icmp", "allow-http", "allow-ssl", "allow-quic"] + + name: + description: + - List name. + required: false + + extended_log: + description: + - Enable/disable extended logging. + - choice | disable | Disable setting. + - choice | enable | Enable setting. + required: false + choices: ["disable", "enable"] + + deep_app_inspection: + description: + - Enable/disable deep application inspection. + - choice | disable | Disable deep application inspection. + - choice | enable | Enable deep application inspection. + required: false + choices: ["disable", "enable"] + + comment: + description: + - comments + required: false + + app_replacemsg: + description: + - Enable/disable replacement messages for blocked applications. + - choice | disable | Disable replacement messages for blocked applications. + - choice | enable | Enable replacement messages for blocked applications. + required: false + choices: ["disable", "enable"] + + entries: + description: + - EXPERTS ONLY! KNOWLEDGE OF FMGR JSON API IS REQUIRED! + - List of multiple child objects to be added. Expects a list of dictionaries. + - Dictionaries must use FortiManager API parameters, not the ansible ones listed below. + - If submitted, all other prefixed sub-parameters ARE IGNORED. This object is MUTUALLY EXCLUSIVE with its options. + - We expect that you know what you are doing with these list parameters, and are leveraging the JSON API Guide. + - WHEN IN DOUBT, OMIT THE USE OF THIS PARAMETER + - AND USE THE SUB OPTIONS BELOW INSTEAD TO CREATE OBJECTS WITH MULTIPLE TASKS + required: false + + entries_action: + description: + - Pass or block traffic, or reset connection for traffic from this application. + - choice | pass | Pass or allow matching traffic. + - choice | block | Block or drop matching traffic. + - choice | reset | Reset sessions for matching traffic. + required: false + choices: ["pass", "block", "reset"] + + entries_application: + description: + - ID of allowed applications. + required: false + + entries_behavior: + description: + - Application behavior filter. + required: false + + entries_category: + description: + - Category ID list. + required: false + + entries_log: + description: + - Enable/disable logging for this application list. + - choice | disable | Disable logging. + - choice | enable | Enable logging. + required: false + choices: ["disable", "enable"] + + entries_log_packet: + description: + - Enable/disable packet logging. + - choice | disable | Disable packet logging. + - choice | enable | Enable packet logging. + required: false + choices: ["disable", "enable"] + + entries_per_ip_shaper: + description: + - Per-IP traffic shaper. + required: false + + entries_popularity: + description: + - Application popularity filter (1 - 5, from least to most popular). + - FLAG Based Options. Specify multiple in list form. + - flag | 1 | Popularity level 1. + - flag | 2 | Popularity level 2. + - flag | 3 | Popularity level 3. + - flag | 4 | Popularity level 4. + - flag | 5 | Popularity level 5. + required: false + choices: ["1", "2", "3", "4", "5"] + + entries_protocols: + description: + - Application protocol filter. + required: false + + entries_quarantine: + description: + - Quarantine method. + - choice | none | Quarantine is disabled. + - choice | attacker | Block all traffic sent from attacker's IP address. + - The attacker's IP address is also added to the banned user list. The target's address is not affected. + required: false + choices: ["none", "attacker"] + + entries_quarantine_expiry: + description: + - Duration of quarantine. (Format ###d##h##m, minimum 1m, maximum 364d23h59m, default = 5m). + - Requires quarantine set to attacker. + required: false + + entries_quarantine_log: + description: + - Enable/disable quarantine logging. + - choice | disable | Disable quarantine logging. + - choice | enable | Enable quarantine logging. + required: false + choices: ["disable", "enable"] + + entries_rate_count: + description: + - Count of the rate. + required: false + + entries_rate_duration: + description: + - Duration (sec) of the rate. + required: false + + entries_rate_mode: + description: + - Rate limit mode. + - choice | periodical | Allow configured number of packets every rate-duration. + - choice | continuous | Block packets once the rate is reached. + required: false + choices: ["periodical", "continuous"] + + entries_rate_track: + description: + - Track the packet protocol field. + - choice | none | + - choice | src-ip | Source IP. + - choice | dest-ip | Destination IP. + - choice | dhcp-client-mac | DHCP client. + - choice | dns-domain | DNS domain. + required: false + choices: ["none", "src-ip", "dest-ip", "dhcp-client-mac", "dns-domain"] + + entries_risk: + description: + - Risk, or impact, of allowing traffic from this application to occur 1 - 5; + - (Low, Elevated, Medium, High, and Critical). + required: false + + entries_session_ttl: + description: + - Session TTL (0 = default). + required: false + + entries_shaper: + description: + - Traffic shaper. + required: false + + entries_shaper_reverse: + description: + - Reverse traffic shaper. + required: false + + entries_sub_category: + description: + - Application Sub-category ID list. + required: false + + entries_technology: + description: + - Application technology filter. + required: false + + entries_vendor: + description: + - Application vendor filter. + required: false + + entries_parameters_value: + description: + - Parameter value. + required: false + + +''' + +EXAMPLES = ''' + - name: DELETE Profile + fmgr_secprof_appctrl: + name: "Ansible_Application_Control_Profile" + comment: "Created by Ansible Module TEST" + mode: "delete" + + - name: CREATE Profile + fmgr_secprof_appctrl: + name: "Ansible_Application_Control_Profile" + comment: "Created by Ansible Module TEST" + mode: "set" + entries: [{ + action: "block", + log: "enable", + log-packet: "enable", + protocols: ["1"], + quarantine: "attacker", + quarantine-log: "enable", + }, + {action: "pass", + category: ["2","3","4"]}, + ] +''' + +RETURN = """ +api_result: + description: full API response, includes status code and message + returned: always + type: str +""" + +from ansible.module_utils.basic import AnsibleModule +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 prepare_dict +from ansible.module_utils.network.fortimanager.common import scrub_dict + +############### +# START METHODS +############### + + +def fmgr_application_list_modify(fmgr, paramgram): + """ + fmgr_application_list -- Modifies Application Control Profiles on FortiManager + + :param fmgr: The fmgr object instance from fmgr_utils.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 + """ + # INIT A BASIC OBJECTS + response = DEFAULT_RESULT_OBJ + url = "" + datagram = {} + + # EVAL THE MODE PARAMETER FOR SET OR ADD + if paramgram["mode"] in ['set', 'add', 'update']: + url = '/pm/config/adom/{adom}/obj/application/list'.format(adom=paramgram["adom"]) + datagram = scrub_dict(prepare_dict(paramgram)) + + # EVAL THE MODE PARAMETER FOR DELETE + elif paramgram["mode"] == "delete": + # SET THE CORRECT URL FOR DELETE + url = '/pm/config/adom/{adom}/obj/application/list/{name}'.format(adom=paramgram["adom"], + name=paramgram["name"]) + datagram = {} + + response = fmgr.process_request(url, datagram, paramgram["mode"]) + return response + + +############# +# END METHODS +############# + + +def main(): + argument_spec = dict( + adom=dict(type="str", default="root"), + mode=dict(choices=["add", "set", "delete", "update"], type="str", default="add"), + + unknown_application_log=dict(required=False, type="str", choices=["disable", "enable"]), + unknown_application_action=dict(required=False, type="str", choices=["pass", "block"]), + replacemsg_group=dict(required=False, type="str"), + p2p_black_list=dict(required=False, type="str", choices=["skype", "edonkey", "bittorrent"]), + other_application_log=dict(required=False, type="str", choices=["disable", "enable"]), + other_application_action=dict(required=False, type="str", choices=["pass", "block"]), + options=dict(required=False, type="str", + choices=["allow-dns", "allow-icmp", "allow-http", "allow-ssl", "allow-quic"]), + name=dict(required=False, type="str"), + extended_log=dict(required=False, type="str", choices=["disable", "enable"]), + deep_app_inspection=dict(required=False, type="str", choices=["disable", "enable"]), + comment=dict(required=False, type="str"), + app_replacemsg=dict(required=False, type="str", choices=["disable", "enable"]), + entries=dict(required=False, type="list"), + entries_action=dict(required=False, type="str", choices=["pass", "block", "reset"]), + entries_application=dict(required=False, type="str"), + entries_behavior=dict(required=False, type="str"), + entries_category=dict(required=False, type="str"), + entries_log=dict(required=False, type="str", choices=["disable", "enable"]), + entries_log_packet=dict(required=False, type="str", choices=["disable", "enable"]), + entries_per_ip_shaper=dict(required=False, type="str"), + entries_popularity=dict(required=False, type="str", choices=["1", "2", "3", "4", "5"]), + entries_protocols=dict(required=False, type="str"), + entries_quarantine=dict(required=False, type="str", choices=["none", "attacker"]), + entries_quarantine_expiry=dict(required=False, type="str"), + entries_quarantine_log=dict(required=False, type="str", choices=["disable", "enable"]), + entries_rate_count=dict(required=False, type="int"), + entries_rate_duration=dict(required=False, type="int"), + entries_rate_mode=dict(required=False, type="str", choices=["periodical", "continuous"]), + entries_rate_track=dict(required=False, type="str", + choices=["none", "src-ip", "dest-ip", "dhcp-client-mac", "dns-domain"]), + entries_risk=dict(required=False, type="str"), + entries_session_ttl=dict(required=False, type="int"), + entries_shaper=dict(required=False, type="str"), + entries_shaper_reverse=dict(required=False, type="str"), + entries_sub_category=dict(required=False, type="str"), + entries_technology=dict(required=False, type="str"), + entries_vendor=dict(required=False, type="str"), + + entries_parameters_value=dict(required=False, type="str"), + + ) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False, ) + # MODULE PARAMGRAM + paramgram = { + "mode": module.params["mode"], + "adom": module.params["adom"], + "unknown-application-log": module.params["unknown_application_log"], + "unknown-application-action": module.params["unknown_application_action"], + "replacemsg-group": module.params["replacemsg_group"], + "p2p-black-list": module.params["p2p_black_list"], + "other-application-log": module.params["other_application_log"], + "other-application-action": module.params["other_application_action"], + "options": module.params["options"], + "name": module.params["name"], + "extended-log": module.params["extended_log"], + "deep-app-inspection": module.params["deep_app_inspection"], + "comment": module.params["comment"], + "app-replacemsg": module.params["app_replacemsg"], + "entries": { + "action": module.params["entries_action"], + "application": module.params["entries_application"], + "behavior": module.params["entries_behavior"], + "category": module.params["entries_category"], + "log": module.params["entries_log"], + "log-packet": module.params["entries_log_packet"], + "per-ip-shaper": module.params["entries_per_ip_shaper"], + "popularity": module.params["entries_popularity"], + "protocols": module.params["entries_protocols"], + "quarantine": module.params["entries_quarantine"], + "quarantine-expiry": module.params["entries_quarantine_expiry"], + "quarantine-log": module.params["entries_quarantine_log"], + "rate-count": module.params["entries_rate_count"], + "rate-duration": module.params["entries_rate_duration"], + "rate-mode": module.params["entries_rate_mode"], + "rate-track": module.params["entries_rate_track"], + "risk": module.params["entries_risk"], + "session-ttl": module.params["entries_session_ttl"], + "shaper": module.params["entries_shaper"], + "shaper-reverse": module.params["entries_shaper_reverse"], + "sub-category": module.params["entries_sub_category"], + "technology": module.params["entries_technology"], + "vendor": module.params["entries_vendor"], + "parameters": { + "value": module.params["entries_parameters_value"], + } + } + } + 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) + + list_overrides = ['entries'] + paramgram = fmgr.tools.paramgram_child_list_override(list_overrides=list_overrides, + paramgram=paramgram, module=module) + + results = DEFAULT_RESULT_OBJ + try: + results = fmgr_application_list_modify(fmgr, paramgram) + fmgr.govern_response(module=module, results=results, + 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_secprof_appctrl.json b/test/units/modules/network/fortimanager/fixtures/test_fmgr_secprof_appctrl.json new file mode 100644 index 00000000000..a0333e75bbf --- /dev/null +++ b/test/units/modules/network/fortimanager/fixtures/test_fmgr_secprof_appctrl.json @@ -0,0 +1,250 @@ +{ + "fmgr_application_list_modify": [ + { + "paramgram_used": { + "comment": "Created by Ansible Module TEST", + "other-application-log": null, + "replacemsg-group": null, + "adom": "ansible", + "unknown-application-log": null, + "p2p-black-list": null, + "unknown-application-action": null, + "extended-log": null, + "deep-app-inspection": null, + "mode": "delete", + "other-application-action": null, + "entries": { + "behavior": null, + "rate-duration": null, + "sub-category": null, + "session-ttl": null, + "per-ip-shaper": null, + "category": null, + "log": null, + "parameters": { + "value": null + }, + "technology": null, + "quarantine-expiry": null, + "application": null, + "protocols": null, + "log-packet": null, + "quarantine-log": null, + "vendor": null, + "risk": null, + "rate-count": null, + "quarantine": null, + "popularity": null, + "shaper": null, + "shaper-reverse": null, + "rate-track": null, + "rate-mode": null, + "action": null + }, + "options": null, + "app-replacemsg": null, + "name": "Ansible_Application_Control_Profile" + }, + "datagram_sent": {}, + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/application/list/Ansible_Application_Control_Profile" + }, + "post_method": "delete" + }, + { + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/application/list" + }, + "datagram_sent": { + "comment": "Created by Ansible Module TEST", + "name": "Ansible_Application_Control_Profile", + "entries": [ + { + "quarantine-log": "enable", + "log": "enable", + "quarantine": "attacker", + "action": "block", + "log-packet": "enable", + "protocols": [ + "1" + ] + }, + { + "action": "pass", + "category": [ + "2", + "3", + "4" + ] + } + ] + }, + "paramgram_used": { + "comment": "Created by Ansible Module TEST", + "other-application-log": null, + "replacemsg-group": null, + "p2p-black-list": null, + "unknown-application-log": null, + "adom": "ansible", + "unknown-application-action": null, + "extended-log": null, + "deep-app-inspection": null, + "mode": "set", + "other-application-action": null, + "entries": [ + { + "quarantine-log": "enable", + "log": "enable", + "quarantine": "attacker", + "action": "block", + "log-packet": "enable", + "protocols": [ + "1" + ] + }, + { + "action": "pass", + "category": [ + "2", + "3", + "4" + ] + } + ], + "options": null, + "app-replacemsg": null, + "name": "Ansible_Application_Control_Profile" + }, + "post_method": "set" + }, + { + "paramgram_used": { + "comment": "Created by Ansible Module TEST", + "other-application-log": null, + "replacemsg-group": null, + "adom": "ansible", + "unknown-application-log": null, + "p2p-black-list": null, + "unknown-application-action": null, + "extended-log": null, + "options": null, + "deep-app-inspection": null, + "mode": "delete", + "other-application-action": null, + "entries": { + "behavior": null, + "rate-duration": null, + "sub-category": null, + "session-ttl": null, + "per-ip-shaper": null, + "category": null, + "log": null, + "parameters": { + "value": null + }, + "technology": null, + "quarantine-expiry": null, + "application": null, + "protocols": null, + "log-packet": null, + "quarantine-log": null, + "vendor": null, + "risk": null, + "rate-count": null, + "quarantine": null, + "popularity": null, + "shaper": null, + "shaper-reverse": null, + "rate-track": null, + "rate-mode": null, + "action": null + }, + "app-replacemsg": null, + "name": "Ansible_Application_Ctl_Profile2" + }, + "datagram_sent": {}, + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/application/list/Ansible_Application_Ctl_Profile2" + }, + "post_method": "delete" + }, + { + "raw_response": { + "status": { + "message": "OK", + "code": 0 + }, + "url": "/pm/config/adom/ansible/obj/application/list" + }, + "datagram_sent": { + "comment": "Created by Ansible Module TEST", + "name": "Ansible_Application_Ctl_Profile2", + "entries": { + "quarantine-log": "enable", + "log": "enable", + "quarantine": "attacker", + "action": "pass", + "log-packet": "enable", + "protocols": "['1']" + } + }, + "paramgram_used": { + "comment": "Created by Ansible Module TEST", + "adom": "ansible", + "unknown-application-log": null, + "extended-log": null, + "other-application-action": null, + "entries": { + "rate-duration": null, + "sub-category": null, + "vendor": null, + "technology": null, + "risk": null, + "category": null, + "log": "enable", + "parameters": { + "value": null + }, + "per-ip-shaper": null, + "quarantine-expiry": null, + "application": null, + "protocols": "['1']", + "log-packet": "enable", + "quarantine-log": "enable", + "session-ttl": null, + "behavior": null, + "rate-count": null, + "quarantine": "attacker", + "popularity": null, + "shaper": null, + "shaper-reverse": null, + "rate-track": null, + "rate-mode": null, + "action": "pass" + }, + "replacemsg-group": null, + "other-application-log": null, + "name": "Ansible_Application_Ctl_Profile2", + "p2p-black-list": null, + "unknown-application-action": null, + "deep-app-inspection": null, + "mode": "set", + "app-replacemsg": null, + "options": null + }, + "post_method": "set" + } + ] +} diff --git a/test/units/modules/network/fortimanager/test_fmgr_secprof_appctrl.py b/test/units/modules/network/fortimanager/test_fmgr_secprof_appctrl.py new file mode 100644 index 00000000000..c49ee7a7646 --- /dev/null +++ b/test/units/modules/network/fortimanager/test_fmgr_secprof_appctrl.py @@ -0,0 +1,78 @@ +# 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_secprof_appctrl +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_secprof_appctrl.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_application_list_modify(fixture_data, mocker): + mocker.patch("ansible.module_utils.network.fortimanager.fortimanager.FortiManagerHandler.process_request", + side_effect=fixture_data) + + # Test using fixture 1 # + output = fmgr_secprof_appctrl.fmgr_application_list_modify(fmg_instance, fixture_data[0]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + # Test using fixture 2 # + output = fmgr_secprof_appctrl.fmgr_application_list_modify(fmg_instance, fixture_data[1]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + # Test using fixture 3 # + output = fmgr_secprof_appctrl.fmgr_application_list_modify(fmg_instance, fixture_data[2]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0 + # Test using fixture 4 # + output = fmgr_secprof_appctrl.fmgr_application_list_modify(fmg_instance, fixture_data[3]['paramgram_used']) + assert output['raw_response']['status']['code'] == 0