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_speed_test_schedule
27short_description: Speed test schedule for each interface 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 speed_test_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    state:
68        description:
69            - Indicates whether to create or remove the object.
70        type: str
71        required: true
72        choices:
73            - present
74            - absent
75    system_speed_test_schedule:
76        description:
77            - Speed test schedule for each interface.
78        default: null
79        type: dict
80        suboptions:
81            diffserv:
82                description:
83                    - DSCP used for speed test.
84                type: str
85            interface:
86                description:
87                    - Interface name. Source system.interface.name.
88                required: true
89                type: str
90            schedules:
91                description:
92                    - Schedules for the interface.
93                type: list
94                suboptions:
95                    name:
96                        description:
97                            - Name of a firewall recurring schedule. Source firewall.schedule.recurring.name.
98                        required: true
99                        type: str
100            server_name:
101                description:
102                    - Speed test server name.
103                type: str
104            status:
105                description:
106                    - Enable/disable scheduled speed test.
107                type: str
108                choices:
109                    - disable
110                    - enable
111            update_inbandwidth:
112                description:
113                    - Enable/disable bypassing interface"s inbound bandwidth setting.
114                type: str
115                choices:
116                    - disable
117                    - enable
118            update_inbandwidth_maximum:
119                description:
120                    - Maximum downloading bandwidth (kbps) to be used in a speed test.
121                type: int
122            update_inbandwidth_minimum:
123                description:
124                    - Minimum downloading bandwidth (kbps) to be considered effective.
125                type: int
126            update_outbandwidth:
127                description:
128                    - Enable/disable bypassing interface"s outbound bandwidth setting.
129                type: str
130                choices:
131                    - disable
132                    - enable
133            update_outbandwidth_maximum:
134                description:
135                    - Maximum uploading bandwidth (kbps) to be used in a speed test.
136                type: int
137            update_outbandwidth_minimum:
138                description:
139                    - Minimum uploading bandwidth (kbps) to be considered effective.
140                type: int
141'''
142
143EXAMPLES = '''
144- hosts: fortigates
145  collections:
146    - fortinet.fortios
147  connection: httpapi
148  vars:
149   vdom: "root"
150   ansible_httpapi_use_ssl: yes
151   ansible_httpapi_validate_certs: no
152   ansible_httpapi_port: 443
153  tasks:
154  - name: Speed test schedule for each interface.
155    fortios_system_speed_test_schedule:
156      vdom:  "{{ vdom }}"
157      state: "present"
158      access_token: "<your_own_value>"
159      system_speed_test_schedule:
160        diffserv: "<your_own_value>"
161        interface: "<your_own_value> (source system.interface.name)"
162        schedules:
163         -
164            name: "default_name_6 (source firewall.schedule.recurring.name)"
165        server_name: "<your_own_value>"
166        status: "disable"
167        update_inbandwidth: "disable"
168        update_inbandwidth_maximum: "10"
169        update_inbandwidth_minimum: "11"
170        update_outbandwidth: "disable"
171        update_outbandwidth_maximum: "13"
172        update_outbandwidth_minimum: "14"
173
174'''
175
176RETURN = '''
177build:
178  description: Build number of the fortigate image
179  returned: always
180  type: str
181  sample: '1547'
182http_method:
183  description: Last method used to provision the content into FortiGate
184  returned: always
185  type: str
186  sample: 'PUT'
187http_status:
188  description: Last result given by FortiGate on last operation applied
189  returned: always
190  type: str
191  sample: "200"
192mkey:
193  description: Master key (id) used in the last call to FortiGate
194  returned: success
195  type: str
196  sample: "id"
197name:
198  description: Name of the table used to fulfill the request
199  returned: always
200  type: str
201  sample: "urlfilter"
202path:
203  description: Path of the table used to fulfill the request
204  returned: always
205  type: str
206  sample: "webfilter"
207revision:
208  description: Internal revision number
209  returned: always
210  type: str
211  sample: "17.0.2.10658"
212serial:
213  description: Serial number of the unit
214  returned: always
215  type: str
216  sample: "FGVMEVYYQT3AB5352"
217status:
218  description: Indication of the operation's result
219  returned: always
220  type: str
221  sample: "success"
222vdom:
223  description: Virtual domain used
224  returned: always
225  type: str
226  sample: "root"
227version:
228  description: Version of the FortiGate
229  returned: always
230  type: str
231  sample: "v5.6.3"
232
233'''
234from ansible.module_utils.basic import AnsibleModule
235from ansible.module_utils.connection import Connection
236from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler
237from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi
238from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec
239from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning
240from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG
241from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison
242from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize
243
244
245def filter_system_speed_test_schedule_data(json):
246    option_list = ['diffserv', 'interface', 'schedules',
247                   'server_name', 'status', 'update_inbandwidth',
248                   'update_inbandwidth_maximum', 'update_inbandwidth_minimum', 'update_outbandwidth',
249                   'update_outbandwidth_maximum', 'update_outbandwidth_minimum']
250    dictionary = {}
251
252    for attribute in option_list:
253        if attribute in json and json[attribute] is not None:
254            dictionary[attribute] = json[attribute]
255
256    return dictionary
257
258
259def underscore_to_hyphen(data):
260    if isinstance(data, list):
261        for i, elem in enumerate(data):
262            data[i] = underscore_to_hyphen(elem)
263    elif isinstance(data, dict):
264        new_data = {}
265        for k, v in data.items():
266            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
267        data = new_data
268
269    return data
270
271
272def system_speed_test_schedule(data, fos):
273    vdom = data['vdom']
274
275    state = data['state']
276
277    system_speed_test_schedule_data = data['system_speed_test_schedule']
278    filtered_data = underscore_to_hyphen(filter_system_speed_test_schedule_data(system_speed_test_schedule_data))
279
280    if state == "present" or state is True:
281        return fos.set('system',
282                       'speed-test-schedule',
283                       data=filtered_data,
284                       vdom=vdom)
285
286    elif state == "absent":
287        return fos.delete('system',
288                          'speed-test-schedule',
289                          mkey=filtered_data['interface'],
290                          vdom=vdom)
291    else:
292        fos._module.fail_json(msg='state must be present or absent!')
293
294
295def is_successful_status(status):
296    return status['status'] == "success" or \
297        status['http_method'] == "DELETE" and status['http_status'] == 404
298
299
300def fortios_system(data, fos):
301
302    if data['system_speed_test_schedule']:
303        resp = system_speed_test_schedule(data, fos)
304    else:
305        fos._module.fail_json(msg='missing task body: %s' % ('system_speed_test_schedule'))
306
307    return not is_successful_status(resp), \
308        resp['status'] == "success" and \
309        (resp['revision_changed'] if 'revision_changed' in resp else True), \
310        resp
311
312
313versioned_schema = {
314    "type": "list",
315    "children": {
316        "status": {
317            "type": "string",
318            "options": [
319                {
320                    "value": "disable",
321                    "revisions": {
322                        "v7.0.0": True
323                    }
324                },
325                {
326                    "value": "enable",
327                    "revisions": {
328                        "v7.0.0": True
329                    }
330                }
331            ],
332            "revisions": {
333                "v7.0.0": True
334            }
335        },
336        "diffserv": {
337            "type": "string",
338            "revisions": {
339                "v7.0.0": True
340            }
341        },
342        "server_name": {
343            "type": "string",
344            "revisions": {
345                "v7.0.0": True
346            }
347        },
348        "update_inbandwidth_minimum": {
349            "type": "integer",
350            "revisions": {
351                "v7.0.0": True
352            }
353        },
354        "schedules": {
355            "type": "list",
356            "children": {
357                "name": {
358                    "type": "string",
359                    "revisions": {
360                        "v7.0.0": True
361                    }
362                }
363            },
364            "revisions": {
365                "v7.0.0": True
366            }
367        },
368        "update_inbandwidth_maximum": {
369            "type": "integer",
370            "revisions": {
371                "v7.0.0": True
372            }
373        },
374        "update_outbandwidth_maximum": {
375            "type": "integer",
376            "revisions": {
377                "v7.0.0": True
378            }
379        },
380        "update_inbandwidth": {
381            "type": "string",
382            "options": [
383                {
384                    "value": "disable",
385                    "revisions": {
386                        "v7.0.0": True
387                    }
388                },
389                {
390                    "value": "enable",
391                    "revisions": {
392                        "v7.0.0": True
393                    }
394                }
395            ],
396            "revisions": {
397                "v7.0.0": True
398            }
399        },
400        "update_outbandwidth": {
401            "type": "string",
402            "options": [
403                {
404                    "value": "disable",
405                    "revisions": {
406                        "v7.0.0": True
407                    }
408                },
409                {
410                    "value": "enable",
411                    "revisions": {
412                        "v7.0.0": True
413                    }
414                }
415            ],
416            "revisions": {
417                "v7.0.0": True
418            }
419        },
420        "interface": {
421            "type": "string",
422            "revisions": {
423                "v7.0.0": True
424            }
425        },
426        "update_outbandwidth_minimum": {
427            "type": "integer",
428            "revisions": {
429                "v7.0.0": True
430            }
431        }
432    },
433    "revisions": {
434        "v7.0.0": True
435    }
436}
437
438
439def main():
440    module_spec = schema_to_module_spec(versioned_schema)
441    mkeyname = 'interface'
442    fields = {
443        "access_token": {"required": False, "type": "str", "no_log": True},
444        "enable_log": {"required": False, "type": bool},
445        "vdom": {"required": False, "type": "str", "default": "root"},
446        "state": {"required": True, "type": "str",
447                  "choices": ["present", "absent"]},
448        "system_speed_test_schedule": {
449            "required": False, "type": "dict", "default": None,
450            "options": {
451            }
452        }
453    }
454    for attribute_name in module_spec['options']:
455        fields["system_speed_test_schedule"]['options'][attribute_name] = module_spec['options'][attribute_name]
456        if mkeyname and mkeyname == attribute_name:
457            fields["system_speed_test_schedule"]['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, "system_speed_test_schedule")
475
476        is_error, has_changed, result = fortios_system(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