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_system_fm
27short_description: Configure FM 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 system feature and fm 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    system_fm:
68        description:
69            - Configure FM.
70        default: null
71        type: dict
72        suboptions:
73            auto_backup:
74                description:
75                    - Enable/disable automatic backup.
76                type: str
77                choices:
78                    - enable
79                    - disable
80            id:
81                description:
82                    - ID.
83                type: str
84            ip:
85                description:
86                    - IP address.
87                type: str
88            ipsec:
89                description:
90                    - Enable/disable IPsec.
91                type: str
92                choices:
93                    - enable
94                    - disable
95            scheduled_config_restore:
96                description:
97                    - Enable/disable scheduled configuration restore.
98                type: str
99                choices:
100                    - enable
101                    - disable
102            status:
103                description:
104                    - Enable/disable FM.
105                type: str
106                choices:
107                    - enable
108                    - disable
109            vdom:
110                description:
111                    - VDOM. Source system.vdom.name.
112                type: str
113'''
114
115EXAMPLES = '''
116- hosts: fortigates
117  collections:
118    - fortinet.fortios
119  connection: httpapi
120  vars:
121   vdom: "root"
122   ansible_httpapi_use_ssl: yes
123   ansible_httpapi_validate_certs: no
124   ansible_httpapi_port: 443
125  tasks:
126  - name: Configure FM.
127    fortios_system_fm:
128      vdom:  "{{ vdom }}"
129      system_fm:
130        auto_backup: "enable"
131        id:  "4"
132        ip: "<your_own_value>"
133        ipsec: "enable"
134        scheduled_config_restore: "enable"
135        status: "enable"
136        vdom: "<your_own_value> (source system.vdom.name)"
137
138'''
139
140RETURN = '''
141build:
142  description: Build number of the fortigate image
143  returned: always
144  type: str
145  sample: '1547'
146http_method:
147  description: Last method used to provision the content into FortiGate
148  returned: always
149  type: str
150  sample: 'PUT'
151http_status:
152  description: Last result given by FortiGate on last operation applied
153  returned: always
154  type: str
155  sample: "200"
156mkey:
157  description: Master key (id) used in the last call to FortiGate
158  returned: success
159  type: str
160  sample: "id"
161name:
162  description: Name of the table used to fulfill the request
163  returned: always
164  type: str
165  sample: "urlfilter"
166path:
167  description: Path of the table used to fulfill the request
168  returned: always
169  type: str
170  sample: "webfilter"
171revision:
172  description: Internal revision number
173  returned: always
174  type: str
175  sample: "17.0.2.10658"
176serial:
177  description: Serial number of the unit
178  returned: always
179  type: str
180  sample: "FGVMEVYYQT3AB5352"
181status:
182  description: Indication of the operation's result
183  returned: always
184  type: str
185  sample: "success"
186vdom:
187  description: Virtual domain used
188  returned: always
189  type: str
190  sample: "root"
191version:
192  description: Version of the FortiGate
193  returned: always
194  type: str
195  sample: "v5.6.3"
196
197'''
198from ansible.module_utils.basic import AnsibleModule
199from ansible.module_utils.connection import Connection
200from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler
201from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi
202from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec
203from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning
204from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG
205from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison
206from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize
207
208
209def filter_system_fm_data(json):
210    option_list = ['auto_backup', 'id', 'ip',
211                   'ipsec', 'scheduled_config_restore', 'status',
212                   'vdom']
213    dictionary = {}
214
215    for attribute in option_list:
216        if attribute in json and json[attribute] is not None:
217            dictionary[attribute] = json[attribute]
218
219    return dictionary
220
221
222def underscore_to_hyphen(data):
223    if isinstance(data, list):
224        for i, elem in enumerate(data):
225            data[i] = underscore_to_hyphen(elem)
226    elif isinstance(data, dict):
227        new_data = {}
228        for k, v in data.items():
229            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
230        data = new_data
231
232    return data
233
234
235def system_fm(data, fos):
236    vdom = data['vdom']
237    system_fm_data = data['system_fm']
238    filtered_data = underscore_to_hyphen(filter_system_fm_data(system_fm_data))
239
240    return fos.set('system',
241                   'fm',
242                   data=filtered_data,
243                   vdom=vdom)
244
245
246def is_successful_status(status):
247    return status['status'] == "success" or \
248        status['http_method'] == "DELETE" and status['http_status'] == 404
249
250
251def fortios_system(data, fos):
252
253    if data['system_fm']:
254        resp = system_fm(data, fos)
255    else:
256        fos._module.fail_json(msg='missing task body: %s' % ('system_fm'))
257
258    return not is_successful_status(resp), \
259        resp['status'] == "success" and \
260        (resp['revision_changed'] if 'revision_changed' in resp else True), \
261        resp
262
263
264versioned_schema = {
265    "type": "dict",
266    "children": {
267        "status": {
268            "type": "string",
269            "options": [
270                {
271                    "value": "enable",
272                    "revisions": {
273                        "v6.0.0": True,
274                        "v7.0.0": True,
275                        "v6.0.5": True,
276                        "v6.4.4": True,
277                        "v6.4.0": True,
278                        "v6.4.1": True,
279                        "v6.2.0": True,
280                        "v6.2.3": True,
281                        "v6.2.5": True,
282                        "v6.2.7": True,
283                        "v6.0.11": True
284                    }
285                },
286                {
287                    "value": "disable",
288                    "revisions": {
289                        "v6.0.0": True,
290                        "v7.0.0": True,
291                        "v6.0.5": True,
292                        "v6.4.4": True,
293                        "v6.4.0": True,
294                        "v6.4.1": True,
295                        "v6.2.0": True,
296                        "v6.2.3": True,
297                        "v6.2.5": True,
298                        "v6.2.7": True,
299                        "v6.0.11": True
300                    }
301                }
302            ],
303            "revisions": {
304                "v6.0.0": True,
305                "v7.0.0": True,
306                "v6.0.5": True,
307                "v6.4.4": True,
308                "v6.4.0": True,
309                "v6.4.1": True,
310                "v6.2.0": True,
311                "v6.2.3": True,
312                "v6.2.5": True,
313                "v6.2.7": True,
314                "v6.0.11": True
315            }
316        },
317        "scheduled_config_restore": {
318            "type": "string",
319            "options": [
320                {
321                    "value": "enable",
322                    "revisions": {
323                        "v6.0.0": True,
324                        "v7.0.0": True,
325                        "v6.0.5": True,
326                        "v6.4.4": True,
327                        "v6.4.0": True,
328                        "v6.4.1": True,
329                        "v6.2.0": True,
330                        "v6.2.3": True,
331                        "v6.2.5": True,
332                        "v6.2.7": True,
333                        "v6.0.11": True
334                    }
335                },
336                {
337                    "value": "disable",
338                    "revisions": {
339                        "v6.0.0": True,
340                        "v7.0.0": True,
341                        "v6.0.5": True,
342                        "v6.4.4": True,
343                        "v6.4.0": True,
344                        "v6.4.1": True,
345                        "v6.2.0": True,
346                        "v6.2.3": True,
347                        "v6.2.5": True,
348                        "v6.2.7": True,
349                        "v6.0.11": True
350                    }
351                }
352            ],
353            "revisions": {
354                "v6.0.0": True,
355                "v7.0.0": True,
356                "v6.0.5": True,
357                "v6.4.4": True,
358                "v6.4.0": True,
359                "v6.4.1": True,
360                "v6.2.0": True,
361                "v6.2.3": True,
362                "v6.2.5": True,
363                "v6.2.7": True,
364                "v6.0.11": True
365            }
366        },
367        "auto_backup": {
368            "type": "string",
369            "options": [
370                {
371                    "value": "enable",
372                    "revisions": {
373                        "v6.0.0": True,
374                        "v7.0.0": True,
375                        "v6.0.5": True,
376                        "v6.4.4": True,
377                        "v6.4.0": True,
378                        "v6.4.1": True,
379                        "v6.2.0": True,
380                        "v6.2.3": True,
381                        "v6.2.5": True,
382                        "v6.2.7": True,
383                        "v6.0.11": True
384                    }
385                },
386                {
387                    "value": "disable",
388                    "revisions": {
389                        "v6.0.0": True,
390                        "v7.0.0": True,
391                        "v6.0.5": True,
392                        "v6.4.4": True,
393                        "v6.4.0": True,
394                        "v6.4.1": True,
395                        "v6.2.0": True,
396                        "v6.2.3": True,
397                        "v6.2.5": True,
398                        "v6.2.7": True,
399                        "v6.0.11": True
400                    }
401                }
402            ],
403            "revisions": {
404                "v6.0.0": True,
405                "v7.0.0": True,
406                "v6.0.5": True,
407                "v6.4.4": True,
408                "v6.4.0": True,
409                "v6.4.1": True,
410                "v6.2.0": True,
411                "v6.2.3": True,
412                "v6.2.5": True,
413                "v6.2.7": True,
414                "v6.0.11": True
415            }
416        },
417        "ip": {
418            "type": "string",
419            "revisions": {
420                "v6.0.0": True,
421                "v7.0.0": True,
422                "v6.0.5": True,
423                "v6.4.4": True,
424                "v6.4.0": True,
425                "v6.4.1": True,
426                "v6.2.0": True,
427                "v6.2.3": True,
428                "v6.2.5": True,
429                "v6.2.7": True,
430                "v6.0.11": True
431            }
432        },
433        "id": {
434            "type": "string",
435            "revisions": {
436                "v6.0.0": True,
437                "v7.0.0": True,
438                "v6.0.5": True,
439                "v6.4.4": True,
440                "v6.4.0": True,
441                "v6.4.1": True,
442                "v6.2.0": True,
443                "v6.2.3": True,
444                "v6.2.5": True,
445                "v6.2.7": True,
446                "v6.0.11": True
447            }
448        },
449        "vdom": {
450            "type": "string",
451            "revisions": {
452                "v6.0.0": True,
453                "v7.0.0": True,
454                "v6.0.5": True,
455                "v6.4.4": True,
456                "v6.4.0": True,
457                "v6.4.1": True,
458                "v6.2.0": True,
459                "v6.2.3": True,
460                "v6.2.5": True,
461                "v6.2.7": True,
462                "v6.0.11": True
463            }
464        },
465        "ipsec": {
466            "type": "string",
467            "options": [
468                {
469                    "value": "enable",
470                    "revisions": {
471                        "v6.0.0": True,
472                        "v7.0.0": True,
473                        "v6.0.5": True,
474                        "v6.4.4": True,
475                        "v6.4.0": True,
476                        "v6.4.1": True,
477                        "v6.2.0": True,
478                        "v6.2.3": True,
479                        "v6.2.5": True,
480                        "v6.2.7": True,
481                        "v6.0.11": True
482                    }
483                },
484                {
485                    "value": "disable",
486                    "revisions": {
487                        "v6.0.0": True,
488                        "v7.0.0": True,
489                        "v6.0.5": True,
490                        "v6.4.4": True,
491                        "v6.4.0": True,
492                        "v6.4.1": True,
493                        "v6.2.0": True,
494                        "v6.2.3": True,
495                        "v6.2.5": True,
496                        "v6.2.7": True,
497                        "v6.0.11": True
498                    }
499                }
500            ],
501            "revisions": {
502                "v6.0.0": True,
503                "v7.0.0": True,
504                "v6.0.5": True,
505                "v6.4.4": True,
506                "v6.4.0": True,
507                "v6.4.1": True,
508                "v6.2.0": True,
509                "v6.2.3": True,
510                "v6.2.5": True,
511                "v6.2.7": True,
512                "v6.0.11": True
513            }
514        }
515    },
516    "revisions": {
517        "v6.0.0": True,
518        "v7.0.0": True,
519        "v6.0.5": True,
520        "v6.4.4": True,
521        "v6.4.0": True,
522        "v6.4.1": True,
523        "v6.2.0": True,
524        "v6.2.3": True,
525        "v6.2.5": True,
526        "v6.2.7": True,
527        "v6.0.11": True
528    }
529}
530
531
532def main():
533    module_spec = schema_to_module_spec(versioned_schema)
534    mkeyname = None
535    fields = {
536        "access_token": {"required": False, "type": "str", "no_log": True},
537        "enable_log": {"required": False, "type": bool},
538        "vdom": {"required": False, "type": "str", "default": "root"},
539        "system_fm": {
540            "required": False, "type": "dict", "default": None,
541            "options": {
542            }
543        }
544    }
545    for attribute_name in module_spec['options']:
546        fields["system_fm"]['options'][attribute_name] = module_spec['options'][attribute_name]
547        if mkeyname and mkeyname == attribute_name:
548            fields["system_fm"]['options'][attribute_name]['required'] = True
549
550    check_legacy_fortiosapi()
551    module = AnsibleModule(argument_spec=fields,
552                           supports_check_mode=False)
553
554    versions_check_result = None
555    if module._socket_path:
556        connection = Connection(module._socket_path)
557        if 'access_token' in module.params:
558            connection.set_option('access_token', module.params['access_token'])
559
560        if 'enable_log' in module.params:
561            connection.set_option('enable_log', module.params['enable_log'])
562        else:
563            connection.set_option('enable_log', False)
564        fos = FortiOSHandler(connection, module, mkeyname)
565        versions_check_result = check_schema_versioning(fos, versioned_schema, "system_fm")
566
567        is_error, has_changed, result = fortios_system(module.params, fos)
568
569    else:
570        module.fail_json(**FAIL_SOCKET_MSG)
571
572    if versions_check_result and versions_check_result['matched'] is False:
573        module.warn("Ansible has detected version mismatch between FortOS system and your playbook, see more details by specifying option -vvv")
574
575    if not is_error:
576        if versions_check_result and versions_check_result['matched'] is False:
577            module.exit_json(changed=has_changed, version_check_warning=versions_check_result, meta=result)
578        else:
579            module.exit_json(changed=has_changed, meta=result)
580    else:
581        if versions_check_result and versions_check_result['matched'] is False:
582            module.fail_json(msg="Error in repo", version_check_warning=versions_check_result, meta=result)
583        else:
584            module.fail_json(msg="Error in repo", meta=result)
585
586
587if __name__ == '__main__':
588    main()
589