1#!/usr/local/bin/python3.8
2from __future__ import (absolute_import, division, print_function)
3# Copyright 2019-2020 Fortinet, Inc.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, either version 3 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
18__metaclass__ = type
19
20ANSIBLE_METADATA = {'status': ['preview'],
21                    'supported_by': 'community',
22                    'metadata_version': '1.1'}
23
24DOCUMENTATION = '''
25---
26module: fortios_ips_settings
27short_description: Configure IPS VDOM parameter in Fortinet's FortiOS and FortiGate.
28description:
29    - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the
30      user to set and modify ips feature and settings category.
31      Examples include all parameters and values need to be adjusted to datasources before usage.
32      Tested with FOS v6.0.0
33version_added: "2.10"
34author:
35    - Link Zheng (@chillancezen)
36    - Jie Xue (@JieX19)
37    - Hongbin Lu (@fgtdev-hblu)
38    - Frank Shen (@frankshen01)
39    - Miguel Angel Munoz (@mamunozgonzalez)
40    - Nicolas Thomas (@thomnico)
41notes:
42    - Legacy fortiosapi has been deprecated, httpapi is the preferred way to run playbooks
43
44requirements:
45    - ansible>=2.9.0
46options:
47    access_token:
48        description:
49            - Token-based authentication.
50              Generated from GUI of Fortigate.
51        type: str
52        required: false
53    enable_log:
54        description:
55            - Enable/Disable logging for task.
56        type: bool
57        required: false
58        default: false
59    vdom:
60        description:
61            - Virtual domain, among those defined previously. A vdom is a
62              virtual instance of the FortiGate that can be configured and
63              used as a different unit.
64        type: str
65        default: root
66
67    ips_settings:
68        description:
69            - Configure IPS VDOM parameter.
70        default: null
71        type: dict
72        suboptions:
73            ips_packet_quota:
74                description:
75                    - Maximum amount of disk space in MB for logged packets when logging to disk. Range depends on disk size.
76                type: int
77            packet_log_history:
78                description:
79                    - Number of packets to capture before and including the one in which the IPS signature is detected (1 - 255).
80                type: int
81            packet_log_memory:
82                description:
83                    - Maximum memory can be used by packet log (64 - 8192 kB).
84                type: int
85            packet_log_post_attack:
86                description:
87                    - Number of packets to log after the IPS signature is detected (0 - 255).
88                type: int
89'''
90
91EXAMPLES = '''
92- hosts: fortigates
93  collections:
94    - fortinet.fortios
95  connection: httpapi
96  vars:
97   vdom: "root"
98   ansible_httpapi_use_ssl: yes
99   ansible_httpapi_validate_certs: no
100   ansible_httpapi_port: 443
101  tasks:
102  - name: Configure IPS VDOM parameter.
103    fortios_ips_settings:
104      vdom:  "{{ vdom }}"
105      ips_settings:
106        ips_packet_quota: "3"
107        packet_log_history: "4"
108        packet_log_memory: "5"
109        packet_log_post_attack: "6"
110
111'''
112
113RETURN = '''
114build:
115  description: Build number of the fortigate image
116  returned: always
117  type: str
118  sample: '1547'
119http_method:
120  description: Last method used to provision the content into FortiGate
121  returned: always
122  type: str
123  sample: 'PUT'
124http_status:
125  description: Last result given by FortiGate on last operation applied
126  returned: always
127  type: str
128  sample: "200"
129mkey:
130  description: Master key (id) used in the last call to FortiGate
131  returned: success
132  type: str
133  sample: "id"
134name:
135  description: Name of the table used to fulfill the request
136  returned: always
137  type: str
138  sample: "urlfilter"
139path:
140  description: Path of the table used to fulfill the request
141  returned: always
142  type: str
143  sample: "webfilter"
144revision:
145  description: Internal revision number
146  returned: always
147  type: str
148  sample: "17.0.2.10658"
149serial:
150  description: Serial number of the unit
151  returned: always
152  type: str
153  sample: "FGVMEVYYQT3AB5352"
154status:
155  description: Indication of the operation's result
156  returned: always
157  type: str
158  sample: "success"
159vdom:
160  description: Virtual domain used
161  returned: always
162  type: str
163  sample: "root"
164version:
165  description: Version of the FortiGate
166  returned: always
167  type: str
168  sample: "v5.6.3"
169
170'''
171from ansible.module_utils.basic import AnsibleModule
172from ansible.module_utils.connection import Connection
173from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler
174from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi
175from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec
176from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning
177from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG
178from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison
179from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize
180
181
182def filter_ips_settings_data(json):
183    option_list = ['ips_packet_quota', 'packet_log_history', 'packet_log_memory',
184                   'packet_log_post_attack']
185    dictionary = {}
186
187    for attribute in option_list:
188        if attribute in json and json[attribute] is not None:
189            dictionary[attribute] = json[attribute]
190
191    return dictionary
192
193
194def underscore_to_hyphen(data):
195    if isinstance(data, list):
196        for i, elem in enumerate(data):
197            data[i] = underscore_to_hyphen(elem)
198    elif isinstance(data, dict):
199        new_data = {}
200        for k, v in data.items():
201            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
202        data = new_data
203
204    return data
205
206
207def ips_settings(data, fos):
208    vdom = data['vdom']
209    ips_settings_data = data['ips_settings']
210    filtered_data = underscore_to_hyphen(filter_ips_settings_data(ips_settings_data))
211
212    return fos.set('ips',
213                   'settings',
214                   data=filtered_data,
215                   vdom=vdom)
216
217
218def is_successful_status(status):
219    return status['status'] == "success" or \
220        status['http_method'] == "DELETE" and status['http_status'] == 404
221
222
223def fortios_ips(data, fos):
224
225    if data['ips_settings']:
226        resp = ips_settings(data, fos)
227    else:
228        fos._module.fail_json(msg='missing task body: %s' % ('ips_settings'))
229
230    return not is_successful_status(resp), \
231        resp['status'] == "success" and \
232        (resp['revision_changed'] if 'revision_changed' in resp else True), \
233        resp
234
235
236versioned_schema = {
237    "type": "dict",
238    "children": {
239        "ips_packet_quota": {
240            "type": "integer",
241            "revisions": {
242                "v6.0.0": True,
243                "v7.0.0": True,
244                "v6.0.5": True,
245                "v6.4.4": True,
246                "v6.4.0": True,
247                "v6.4.1": True,
248                "v6.2.0": True,
249                "v6.2.3": True,
250                "v6.2.5": True,
251                "v6.2.7": True,
252                "v6.0.11": True
253            }
254        },
255        "packet_log_history": {
256            "type": "integer",
257            "revisions": {
258                "v6.0.0": True,
259                "v7.0.0": True,
260                "v6.0.5": True,
261                "v6.4.4": True,
262                "v6.4.0": True,
263                "v6.4.1": True,
264                "v6.2.0": True,
265                "v6.2.3": True,
266                "v6.2.5": True,
267                "v6.2.7": True,
268                "v6.0.11": True
269            }
270        },
271        "packet_log_post_attack": {
272            "type": "integer",
273            "revisions": {
274                "v6.0.0": True,
275                "v7.0.0": True,
276                "v6.0.5": True,
277                "v6.4.4": True,
278                "v6.4.0": True,
279                "v6.4.1": True,
280                "v6.2.0": True,
281                "v6.2.3": True,
282                "v6.2.5": True,
283                "v6.2.7": True,
284                "v6.0.11": True
285            }
286        },
287        "packet_log_memory": {
288            "type": "integer",
289            "revisions": {
290                "v6.0.0": True,
291                "v7.0.0": True,
292                "v6.0.5": True,
293                "v6.4.4": True,
294                "v6.4.0": True,
295                "v6.4.1": True,
296                "v6.2.0": True,
297                "v6.2.3": True,
298                "v6.2.5": True,
299                "v6.2.7": True,
300                "v6.0.11": True
301            }
302        }
303    },
304    "revisions": {
305        "v6.0.0": True,
306        "v7.0.0": True,
307        "v6.0.5": True,
308        "v6.4.4": True,
309        "v6.4.0": True,
310        "v6.4.1": True,
311        "v6.2.0": True,
312        "v6.2.3": True,
313        "v6.2.5": True,
314        "v6.2.7": True,
315        "v6.0.11": True
316    }
317}
318
319
320def main():
321    module_spec = schema_to_module_spec(versioned_schema)
322    mkeyname = None
323    fields = {
324        "access_token": {"required": False, "type": "str", "no_log": True},
325        "enable_log": {"required": False, "type": bool},
326        "vdom": {"required": False, "type": "str", "default": "root"},
327        "ips_settings": {
328            "required": False, "type": "dict", "default": None,
329            "options": {
330            }
331        }
332    }
333    for attribute_name in module_spec['options']:
334        fields["ips_settings"]['options'][attribute_name] = module_spec['options'][attribute_name]
335        if mkeyname and mkeyname == attribute_name:
336            fields["ips_settings"]['options'][attribute_name]['required'] = True
337
338    check_legacy_fortiosapi()
339    module = AnsibleModule(argument_spec=fields,
340                           supports_check_mode=False)
341
342    versions_check_result = None
343    if module._socket_path:
344        connection = Connection(module._socket_path)
345        if 'access_token' in module.params:
346            connection.set_option('access_token', module.params['access_token'])
347
348        if 'enable_log' in module.params:
349            connection.set_option('enable_log', module.params['enable_log'])
350        else:
351            connection.set_option('enable_log', False)
352        fos = FortiOSHandler(connection, module, mkeyname)
353        versions_check_result = check_schema_versioning(fos, versioned_schema, "ips_settings")
354
355        is_error, has_changed, result = fortios_ips(module.params, fos)
356
357    else:
358        module.fail_json(**FAIL_SOCKET_MSG)
359
360    if versions_check_result and versions_check_result['matched'] is False:
361        module.warn("Ansible has detected version mismatch between FortOS system and your playbook, see more details by specifying option -vvv")
362
363    if not is_error:
364        if versions_check_result and versions_check_result['matched'] is False:
365            module.exit_json(changed=has_changed, version_check_warning=versions_check_result, meta=result)
366        else:
367            module.exit_json(changed=has_changed, meta=result)
368    else:
369        if versions_check_result and versions_check_result['matched'] is False:
370            module.fail_json(msg="Error in repo", version_check_warning=versions_check_result, meta=result)
371        else:
372            module.fail_json(msg="Error in repo", meta=result)
373
374
375if __name__ == '__main__':
376    main()
377