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_switch_controller_system
27short_description: Configure system-wide switch controller settings 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 switch_controller feature and system 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    switch_controller_system:
68        description:
69            - Configure system-wide switch controller settings.
70        default: null
71        type: dict
72        suboptions:
73            data_sync_interval:
74                description:
75                    - Time interval between collection of switch data (30 - 1800 sec).
76                type: int
77            dynamic_periodic_interval:
78                description:
79                    - Periodic time interval to run Dynamic port policy engine (5 - 60 sec).
80                type: int
81            iot_holdoff:
82                description:
83                    - MAC entry"s creation time. Time must be greater than this value for an entry to be created (0 - 10080 mins).
84                type: int
85            iot_mac_idle:
86                description:
87                    - MAC entry"s idle time. MAC entry is removed after this value (0 - 10080 mins).
88                type: int
89            iot_scan_interval:
90                description:
91                    - IoT scan interval (2 - 10080 mins).
92                type: int
93            iot_weight_threshold:
94                description:
95                    - MAC entry"s confidence value. Value is re-queried when below this value .
96                type: int
97            nac_periodic_interval:
98                description:
99                    - Periodic time interval to run NAC engine (5 - 60 sec).
100                type: int
101            parallel_process:
102                description:
103                    - Maximum number of parallel processes (1 - 300).
104                type: int
105            parallel_process_override:
106                description:
107                    - Enable/disable parallel process override.
108                type: str
109                choices:
110                    - disable
111                    - enable
112            tunnel_mode:
113                description:
114                    - Compatible/strict tunnel mode.
115                type: str
116                choices:
117                    - compatible
118                    - strict
119'''
120
121EXAMPLES = '''
122- hosts: fortigates
123  collections:
124    - fortinet.fortios
125  connection: httpapi
126  vars:
127   vdom: "root"
128   ansible_httpapi_use_ssl: yes
129   ansible_httpapi_validate_certs: no
130   ansible_httpapi_port: 443
131  tasks:
132  - name: Configure system-wide switch controller settings.
133    fortios_switch_controller_system:
134      vdom:  "{{ vdom }}"
135      switch_controller_system:
136        data_sync_interval: "3"
137        dynamic_periodic_interval: "4"
138        iot_holdoff: "5"
139        iot_mac_idle: "6"
140        iot_scan_interval: "7"
141        iot_weight_threshold: "8"
142        nac_periodic_interval: "9"
143        parallel_process: "10"
144        parallel_process_override: "disable"
145        tunnel_mode: "compatible"
146
147'''
148
149RETURN = '''
150build:
151  description: Build number of the fortigate image
152  returned: always
153  type: str
154  sample: '1547'
155http_method:
156  description: Last method used to provision the content into FortiGate
157  returned: always
158  type: str
159  sample: 'PUT'
160http_status:
161  description: Last result given by FortiGate on last operation applied
162  returned: always
163  type: str
164  sample: "200"
165mkey:
166  description: Master key (id) used in the last call to FortiGate
167  returned: success
168  type: str
169  sample: "id"
170name:
171  description: Name of the table used to fulfill the request
172  returned: always
173  type: str
174  sample: "urlfilter"
175path:
176  description: Path of the table used to fulfill the request
177  returned: always
178  type: str
179  sample: "webfilter"
180revision:
181  description: Internal revision number
182  returned: always
183  type: str
184  sample: "17.0.2.10658"
185serial:
186  description: Serial number of the unit
187  returned: always
188  type: str
189  sample: "FGVMEVYYQT3AB5352"
190status:
191  description: Indication of the operation's result
192  returned: always
193  type: str
194  sample: "success"
195vdom:
196  description: Virtual domain used
197  returned: always
198  type: str
199  sample: "root"
200version:
201  description: Version of the FortiGate
202  returned: always
203  type: str
204  sample: "v5.6.3"
205
206'''
207from ansible.module_utils.basic import AnsibleModule
208from ansible.module_utils.connection import Connection
209from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler
210from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi
211from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec
212from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning
213from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG
214from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison
215from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize
216
217
218def filter_switch_controller_system_data(json):
219    option_list = ['data_sync_interval', 'dynamic_periodic_interval', 'iot_holdoff',
220                   'iot_mac_idle', 'iot_scan_interval', 'iot_weight_threshold',
221                   'nac_periodic_interval', 'parallel_process', 'parallel_process_override',
222                   'tunnel_mode']
223    dictionary = {}
224
225    for attribute in option_list:
226        if attribute in json and json[attribute] is not None:
227            dictionary[attribute] = json[attribute]
228
229    return dictionary
230
231
232def underscore_to_hyphen(data):
233    if isinstance(data, list):
234        for i, elem in enumerate(data):
235            data[i] = underscore_to_hyphen(elem)
236    elif isinstance(data, dict):
237        new_data = {}
238        for k, v in data.items():
239            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
240        data = new_data
241
242    return data
243
244
245def switch_controller_system(data, fos):
246    vdom = data['vdom']
247    switch_controller_system_data = data['switch_controller_system']
248    filtered_data = underscore_to_hyphen(filter_switch_controller_system_data(switch_controller_system_data))
249
250    return fos.set('switch-controller',
251                   'system',
252                   data=filtered_data,
253                   vdom=vdom)
254
255
256def is_successful_status(status):
257    return status['status'] == "success" or \
258        status['http_method'] == "DELETE" and status['http_status'] == 404
259
260
261def fortios_switch_controller(data, fos):
262
263    if data['switch_controller_system']:
264        resp = switch_controller_system(data, fos)
265    else:
266        fos._module.fail_json(msg='missing task body: %s' % ('switch_controller_system'))
267
268    return not is_successful_status(resp), \
269        resp['status'] == "success" and \
270        (resp['revision_changed'] if 'revision_changed' in resp else True), \
271        resp
272
273
274versioned_schema = {
275    "type": "dict",
276    "children": {
277        "tunnel_mode": {
278            "type": "string",
279            "options": [
280                {
281                    "value": "compatible",
282                    "revisions": {
283                        "v7.0.0": True
284                    }
285                },
286                {
287                    "value": "strict",
288                    "revisions": {
289                        "v7.0.0": True
290                    }
291                }
292            ],
293            "revisions": {
294                "v7.0.0": True
295            }
296        },
297        "iot_holdoff": {
298            "type": "integer",
299            "revisions": {
300                "v6.4.4": True,
301                "v7.0.0": True,
302                "v6.4.0": True,
303                "v6.4.1": True
304            }
305        },
306        "iot_scan_interval": {
307            "type": "integer",
308            "revisions": {
309                "v6.4.4": True,
310                "v7.0.0": True,
311                "v6.4.0": True,
312                "v6.4.1": True
313            }
314        },
315        "parallel_process_override": {
316            "type": "string",
317            "options": [
318                {
319                    "value": "disable",
320                    "revisions": {
321                        "v6.0.0": True,
322                        "v7.0.0": True,
323                        "v6.0.5": True,
324                        "v6.4.4": True,
325                        "v6.4.0": True,
326                        "v6.4.1": True,
327                        "v6.2.0": True,
328                        "v6.2.3": True,
329                        "v6.2.5": True,
330                        "v6.2.7": True,
331                        "v6.0.11": True
332                    }
333                },
334                {
335                    "value": "enable",
336                    "revisions": {
337                        "v6.0.0": True,
338                        "v7.0.0": True,
339                        "v6.0.5": True,
340                        "v6.4.4": True,
341                        "v6.4.0": True,
342                        "v6.4.1": True,
343                        "v6.2.0": True,
344                        "v6.2.3": True,
345                        "v6.2.5": True,
346                        "v6.2.7": True,
347                        "v6.0.11": True
348                    }
349                }
350            ],
351            "revisions": {
352                "v6.0.0": True,
353                "v7.0.0": True,
354                "v6.0.5": True,
355                "v6.4.4": True,
356                "v6.4.0": True,
357                "v6.4.1": True,
358                "v6.2.0": True,
359                "v6.2.3": True,
360                "v6.2.5": True,
361                "v6.2.7": True,
362                "v6.0.11": True
363            }
364        },
365        "iot_mac_idle": {
366            "type": "integer",
367            "revisions": {
368                "v6.4.4": True,
369                "v7.0.0": True,
370                "v6.4.0": True,
371                "v6.4.1": True
372            }
373        },
374        "nac_periodic_interval": {
375            "type": "integer",
376            "revisions": {
377                "v7.0.0": True
378            }
379        },
380        "data_sync_interval": {
381            "type": "integer",
382            "revisions": {
383                "v7.0.0": True,
384                "v6.4.4": True,
385                "v6.4.0": True,
386                "v6.4.1": True,
387                "v6.2.0": True,
388                "v6.2.3": True,
389                "v6.2.5": True,
390                "v6.2.7": True
391            }
392        },
393        "dynamic_periodic_interval": {
394            "type": "integer",
395            "revisions": {
396                "v7.0.0": True
397            }
398        },
399        "iot_weight_threshold": {
400            "type": "integer",
401            "revisions": {
402                "v6.4.4": True,
403                "v7.0.0": True,
404                "v6.4.0": True,
405                "v6.4.1": True
406            }
407        },
408        "parallel_process": {
409            "type": "integer",
410            "revisions": {
411                "v6.0.0": True,
412                "v7.0.0": True,
413                "v6.0.5": True,
414                "v6.4.4": True,
415                "v6.4.0": True,
416                "v6.4.1": True,
417                "v6.2.0": True,
418                "v6.2.3": True,
419                "v6.2.5": True,
420                "v6.2.7": True,
421                "v6.0.11": True
422            }
423        }
424    },
425    "revisions": {
426        "v6.0.0": True,
427        "v7.0.0": True,
428        "v6.0.5": True,
429        "v6.4.4": True,
430        "v6.4.0": True,
431        "v6.4.1": True,
432        "v6.2.0": True,
433        "v6.2.3": True,
434        "v6.2.5": True,
435        "v6.2.7": True,
436        "v6.0.11": True
437    }
438}
439
440
441def main():
442    module_spec = schema_to_module_spec(versioned_schema)
443    mkeyname = None
444    fields = {
445        "access_token": {"required": False, "type": "str", "no_log": True},
446        "enable_log": {"required": False, "type": bool},
447        "vdom": {"required": False, "type": "str", "default": "root"},
448        "switch_controller_system": {
449            "required": False, "type": "dict", "default": None,
450            "options": {
451            }
452        }
453    }
454    for attribute_name in module_spec['options']:
455        fields["switch_controller_system"]['options'][attribute_name] = module_spec['options'][attribute_name]
456        if mkeyname and mkeyname == attribute_name:
457            fields["switch_controller_system"]['options'][attribute_name]['required'] = True
458
459    check_legacy_fortiosapi()
460    module = AnsibleModule(argument_spec=fields,
461                           supports_check_mode=False)
462
463    versions_check_result = None
464    if module._socket_path:
465        connection = Connection(module._socket_path)
466        if 'access_token' in module.params:
467            connection.set_option('access_token', module.params['access_token'])
468
469        if 'enable_log' in module.params:
470            connection.set_option('enable_log', module.params['enable_log'])
471        else:
472            connection.set_option('enable_log', False)
473        fos = FortiOSHandler(connection, module, mkeyname)
474        versions_check_result = check_schema_versioning(fos, versioned_schema, "switch_controller_system")
475
476        is_error, has_changed, result = fortios_switch_controller(module.params, fos)
477
478    else:
479        module.fail_json(**FAIL_SOCKET_MSG)
480
481    if versions_check_result and versions_check_result['matched'] is False:
482        module.warn("Ansible has detected version mismatch between FortOS system and your playbook, see more details by specifying option -vvv")
483
484    if not is_error:
485        if versions_check_result and versions_check_result['matched'] is False:
486            module.exit_json(changed=has_changed, version_check_warning=versions_check_result, meta=result)
487        else:
488            module.exit_json(changed=has_changed, meta=result)
489    else:
490        if versions_check_result and versions_check_result['matched'] is False:
491            module.fail_json(msg="Error in repo", version_check_warning=versions_check_result, meta=result)
492        else:
493            module.fail_json(msg="Error in repo", meta=result)
494
495
496if __name__ == '__main__':
497    main()
498