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