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_qos_queue_policy
27short_description: Configure FortiSwitch QoS egress queue policy 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_qos feature and queue_policy 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    switch_controller_qos_queue_policy:
76        description:
77            - Configure FortiSwitch QoS egress queue policy.
78        default: null
79        type: dict
80        suboptions:
81            cos_queue:
82                description:
83                    - COS queue configuration.
84                type: list
85                suboptions:
86                    description:
87                        description:
88                            - Description of the COS queue.
89                        type: str
90                    drop_policy:
91                        description:
92                            - COS queue drop policy.
93                        type: str
94                        choices:
95                            - taildrop
96                            - weighted-random-early-detection
97                    ecn:
98                        description:
99                            - Enable/disable ECN packet marking to drop eligible packets.
100                        type: str
101                        choices:
102                            - disable
103                            - enable
104                    max_rate:
105                        description:
106                            - Maximum rate (0 - 4294967295 kbps, 0 to disable).
107                        type: int
108                    max_rate_percent:
109                        description:
110                            - Maximum rate (% of link speed).
111                        type: int
112                    min_rate:
113                        description:
114                            - Minimum rate (0 - 4294967295 kbps, 0 to disable).
115                        type: int
116                    min_rate_percent:
117                        description:
118                            - Minimum rate (% of link speed).
119                        type: int
120                    name:
121                        description:
122                            - Cos queue ID.
123                        required: true
124                        type: str
125                    weight:
126                        description:
127                            - Weight of weighted round robin scheduling.
128                        type: int
129            name:
130                description:
131                    - QoS policy name
132                required: true
133                type: str
134            rate_by:
135                description:
136                    - COS queue rate by kbps or percent.
137                type: str
138                choices:
139                    - kbps
140                    - percent
141            schedule:
142                description:
143                    - COS queue scheduling.
144                type: str
145                choices:
146                    - strict
147                    - round-robin
148                    - weighted
149'''
150
151EXAMPLES = '''
152- hosts: fortigates
153  collections:
154    - fortinet.fortios
155  connection: httpapi
156  vars:
157   vdom: "root"
158   ansible_httpapi_use_ssl: yes
159   ansible_httpapi_validate_certs: no
160   ansible_httpapi_port: 443
161  tasks:
162  - name: Configure FortiSwitch QoS egress queue policy.
163    fortios_switch_controller_qos_queue_policy:
164      vdom:  "{{ vdom }}"
165      state: "present"
166      access_token: "<your_own_value>"
167      switch_controller_qos_queue_policy:
168        cos_queue:
169         -
170            description: "<your_own_value>"
171            drop_policy: "taildrop"
172            ecn: "disable"
173            max_rate: "7"
174            max_rate_percent: "8"
175            min_rate: "9"
176            min_rate_percent: "10"
177            name: "default_name_11"
178            weight: "12"
179        name: "default_name_13"
180        rate_by: "kbps"
181        schedule: "strict"
182
183'''
184
185RETURN = '''
186build:
187  description: Build number of the fortigate image
188  returned: always
189  type: str
190  sample: '1547'
191http_method:
192  description: Last method used to provision the content into FortiGate
193  returned: always
194  type: str
195  sample: 'PUT'
196http_status:
197  description: Last result given by FortiGate on last operation applied
198  returned: always
199  type: str
200  sample: "200"
201mkey:
202  description: Master key (id) used in the last call to FortiGate
203  returned: success
204  type: str
205  sample: "id"
206name:
207  description: Name of the table used to fulfill the request
208  returned: always
209  type: str
210  sample: "urlfilter"
211path:
212  description: Path of the table used to fulfill the request
213  returned: always
214  type: str
215  sample: "webfilter"
216revision:
217  description: Internal revision number
218  returned: always
219  type: str
220  sample: "17.0.2.10658"
221serial:
222  description: Serial number of the unit
223  returned: always
224  type: str
225  sample: "FGVMEVYYQT3AB5352"
226status:
227  description: Indication of the operation's result
228  returned: always
229  type: str
230  sample: "success"
231vdom:
232  description: Virtual domain used
233  returned: always
234  type: str
235  sample: "root"
236version:
237  description: Version of the FortiGate
238  returned: always
239  type: str
240  sample: "v5.6.3"
241
242'''
243from ansible.module_utils.basic import AnsibleModule
244from ansible.module_utils.connection import Connection
245from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler
246from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi
247from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec
248from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning
249from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG
250from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison
251from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize
252
253
254def filter_switch_controller_qos_queue_policy_data(json):
255    option_list = ['cos_queue', 'name', 'rate_by',
256                   'schedule']
257    dictionary = {}
258
259    for attribute in option_list:
260        if attribute in json and json[attribute] is not None:
261            dictionary[attribute] = json[attribute]
262
263    return dictionary
264
265
266def underscore_to_hyphen(data):
267    if isinstance(data, list):
268        for i, elem in enumerate(data):
269            data[i] = underscore_to_hyphen(elem)
270    elif isinstance(data, dict):
271        new_data = {}
272        for k, v in data.items():
273            new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
274        data = new_data
275
276    return data
277
278
279def switch_controller_qos_queue_policy(data, fos, check_mode=False):
280
281    vdom = data['vdom']
282
283    state = data['state']
284
285    switch_controller_qos_queue_policy_data = data['switch_controller_qos_queue_policy']
286    filtered_data = underscore_to_hyphen(filter_switch_controller_qos_queue_policy_data(switch_controller_qos_queue_policy_data))
287
288    # check_mode starts from here
289    if check_mode:
290        mkey = fos.get_mkey('system', 'interface', filtered_data, vdom=vdom)
291        current_data = fos.get('system', 'interface', vdom=vdom, mkey=mkey)
292        is_existed = current_data and current_data.get('http_status') == 200 \
293            and isinstance(current_data.get('results'), list) \
294            and len(current_data['results']) > 0
295
296        # 2. if it exists and the state is 'present' then compare current settings with desired
297        if state == 'present' or state is True:
298            if mkey is None:
299                return False, True, filtered_data
300
301            # if mkey exists then compare each other
302            # record exits and they're matched or not
303            if is_existed:
304                is_same = is_same_comparison(
305                    serialize(current_data['results'][0]), serialize(filtered_data))
306                return False, not is_same, filtered_data
307
308            # record does not exist
309            return False, True, filtered_data
310
311        if state == 'absent':
312            if mkey is None:
313                return False, False, filtered_data
314
315            if is_existed:
316                return False, True, filtered_data
317            return False, False, filtered_data
318
319        return True, False, {'reason: ': 'Must provide state parameter'}
320
321    if state == "present" or state is True:
322        return fos.set('switch-controller.qos',
323                       'queue-policy',
324                       data=filtered_data,
325                       vdom=vdom)
326
327    elif state == "absent":
328        return fos.delete('switch-controller.qos',
329                          'queue-policy',
330                          mkey=filtered_data['name'],
331                          vdom=vdom)
332    else:
333        fos._module.fail_json(msg='state must be present or absent!')
334
335
336def is_successful_status(status):
337    return status['status'] == "success" or \
338        status['http_method'] == "DELETE" and status['http_status'] == 404
339
340
341def fortios_switch_controller_qos(data, fos, check_mode):
342
343    if data['switch_controller_qos_queue_policy']:
344        resp = switch_controller_qos_queue_policy(data, fos, check_mode)
345    else:
346        fos._module.fail_json(msg='missing task body: %s' % ('switch_controller_qos_queue_policy'))
347    if check_mode:
348        return resp
349    return not is_successful_status(resp), \
350        resp['status'] == "success" and \
351        (resp['revision_changed'] if 'revision_changed' in resp else True), \
352        resp
353
354
355versioned_schema = {
356    "type": "list",
357    "children": {
358        "cos_queue": {
359            "type": "list",
360            "children": {
361                "name": {
362                    "type": "string",
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                "weight": {
378                    "type": "integer",
379                    "revisions": {
380                        "v6.0.0": True,
381                        "v7.0.0": True,
382                        "v6.0.5": True,
383                        "v6.4.4": True,
384                        "v6.4.0": True,
385                        "v6.4.1": True,
386                        "v6.2.0": True,
387                        "v6.2.3": True,
388                        "v6.2.5": True,
389                        "v6.2.7": True,
390                        "v6.0.11": True
391                    }
392                },
393                "min_rate": {
394                    "type": "integer",
395                    "revisions": {
396                        "v6.0.0": True,
397                        "v7.0.0": True,
398                        "v6.0.5": True,
399                        "v6.4.4": True,
400                        "v6.4.0": True,
401                        "v6.4.1": True,
402                        "v6.2.0": True,
403                        "v6.2.3": True,
404                        "v6.2.5": True,
405                        "v6.2.7": True,
406                        "v6.0.11": True
407                    }
408                },
409                "ecn": {
410                    "type": "string",
411                    "options": [
412                        {
413                            "value": "disable",
414                            "revisions": {
415                                "v6.4.4": True,
416                                "v7.0.0": True,
417                                "v6.4.0": True
418                            }
419                        },
420                        {
421                            "value": "enable",
422                            "revisions": {
423                                "v6.4.4": True,
424                                "v7.0.0": True,
425                                "v6.4.0": True
426                            }
427                        }
428                    ],
429                    "revisions": {
430                        "v6.4.4": True,
431                        "v7.0.0": True,
432                        "v6.4.0": True,
433                        "v6.4.1": False
434                    }
435                },
436                "max_rate": {
437                    "type": "integer",
438                    "revisions": {
439                        "v6.0.0": True,
440                        "v7.0.0": True,
441                        "v6.0.5": True,
442                        "v6.4.4": True,
443                        "v6.4.0": True,
444                        "v6.4.1": True,
445                        "v6.2.0": True,
446                        "v6.2.3": True,
447                        "v6.2.5": True,
448                        "v6.2.7": True,
449                        "v6.0.11": True
450                    }
451                },
452                "max_rate_percent": {
453                    "type": "integer",
454                    "revisions": {
455                        "v7.0.0": True,
456                        "v6.4.4": True,
457                        "v6.4.0": True,
458                        "v6.4.1": True,
459                        "v6.2.0": True,
460                        "v6.2.3": True,
461                        "v6.2.5": True,
462                        "v6.2.7": True
463                    }
464                },
465                "drop_policy": {
466                    "type": "string",
467                    "options": [
468                        {
469                            "value": "taildrop",
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": "weighted-random-early-detection",
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                "min_rate_percent": {
516                    "type": "integer",
517                    "revisions": {
518                        "v7.0.0": True,
519                        "v6.4.4": True,
520                        "v6.4.0": True,
521                        "v6.4.1": True,
522                        "v6.2.0": True,
523                        "v6.2.3": True,
524                        "v6.2.5": True,
525                        "v6.2.7": True
526                    }
527                },
528                "description": {
529                    "type": "string",
530                    "revisions": {
531                        "v6.0.0": True,
532                        "v7.0.0": True,
533                        "v6.0.5": True,
534                        "v6.4.4": True,
535                        "v6.4.0": True,
536                        "v6.4.1": True,
537                        "v6.2.0": True,
538                        "v6.2.3": True,
539                        "v6.2.5": True,
540                        "v6.2.7": True,
541                        "v6.0.11": True
542                    }
543                }
544            },
545            "revisions": {
546                "v6.0.0": True,
547                "v7.0.0": True,
548                "v6.0.5": True,
549                "v6.4.4": True,
550                "v6.4.0": True,
551                "v6.4.1": True,
552                "v6.2.0": True,
553                "v6.2.3": True,
554                "v6.2.5": True,
555                "v6.2.7": True,
556                "v6.0.11": True
557            }
558        },
559        "rate_by": {
560            "type": "string",
561            "options": [
562                {
563                    "value": "kbps",
564                    "revisions": {
565                        "v7.0.0": True,
566                        "v6.4.4": True,
567                        "v6.4.0": True,
568                        "v6.4.1": True,
569                        "v6.2.0": True,
570                        "v6.2.3": True,
571                        "v6.2.5": True,
572                        "v6.2.7": True
573                    }
574                },
575                {
576                    "value": "percent",
577                    "revisions": {
578                        "v7.0.0": True,
579                        "v6.4.4": True,
580                        "v6.4.0": True,
581                        "v6.4.1": True,
582                        "v6.2.0": True,
583                        "v6.2.3": True,
584                        "v6.2.5": True,
585                        "v6.2.7": True
586                    }
587                }
588            ],
589            "revisions": {
590                "v7.0.0": True,
591                "v6.4.4": True,
592                "v6.4.0": True,
593                "v6.4.1": True,
594                "v6.2.0": True,
595                "v6.2.3": True,
596                "v6.2.5": True,
597                "v6.2.7": True
598            }
599        },
600        "name": {
601            "type": "string",
602            "revisions": {
603                "v6.0.0": True,
604                "v7.0.0": True,
605                "v6.0.5": True,
606                "v6.4.4": True,
607                "v6.4.0": True,
608                "v6.4.1": True,
609                "v6.2.0": True,
610                "v6.2.3": True,
611                "v6.2.5": True,
612                "v6.2.7": True,
613                "v6.0.11": True
614            }
615        },
616        "schedule": {
617            "type": "string",
618            "options": [
619                {
620                    "value": "strict",
621                    "revisions": {
622                        "v6.0.0": True,
623                        "v7.0.0": True,
624                        "v6.0.5": True,
625                        "v6.4.4": True,
626                        "v6.4.0": True,
627                        "v6.4.1": True,
628                        "v6.2.0": True,
629                        "v6.2.3": True,
630                        "v6.2.5": True,
631                        "v6.2.7": True,
632                        "v6.0.11": True
633                    }
634                },
635                {
636                    "value": "round-robin",
637                    "revisions": {
638                        "v6.0.0": True,
639                        "v7.0.0": True,
640                        "v6.0.5": True,
641                        "v6.4.4": True,
642                        "v6.4.0": True,
643                        "v6.4.1": True,
644                        "v6.2.0": True,
645                        "v6.2.3": True,
646                        "v6.2.5": True,
647                        "v6.2.7": True,
648                        "v6.0.11": True
649                    }
650                },
651                {
652                    "value": "weighted",
653                    "revisions": {
654                        "v6.0.0": True,
655                        "v7.0.0": True,
656                        "v6.0.5": True,
657                        "v6.4.4": True,
658                        "v6.4.0": True,
659                        "v6.4.1": True,
660                        "v6.2.0": True,
661                        "v6.2.3": True,
662                        "v6.2.5": True,
663                        "v6.2.7": True,
664                        "v6.0.11": True
665                    }
666                }
667            ],
668            "revisions": {
669                "v6.0.0": True,
670                "v7.0.0": True,
671                "v6.0.5": True,
672                "v6.4.4": True,
673                "v6.4.0": True,
674                "v6.4.1": True,
675                "v6.2.0": True,
676                "v6.2.3": True,
677                "v6.2.5": True,
678                "v6.2.7": True,
679                "v6.0.11": True
680            }
681        }
682    },
683    "revisions": {
684        "v6.0.0": True,
685        "v7.0.0": True,
686        "v6.0.5": True,
687        "v6.4.4": True,
688        "v6.4.0": True,
689        "v6.4.1": True,
690        "v6.2.0": True,
691        "v6.2.3": True,
692        "v6.2.5": True,
693        "v6.2.7": True,
694        "v6.0.11": True
695    }
696}
697
698
699def main():
700    module_spec = schema_to_module_spec(versioned_schema)
701    mkeyname = 'name'
702    fields = {
703        "access_token": {"required": False, "type": "str", "no_log": True},
704        "enable_log": {"required": False, "type": bool},
705        "vdom": {"required": False, "type": "str", "default": "root"},
706        "state": {"required": True, "type": "str",
707                  "choices": ["present", "absent"]},
708        "switch_controller_qos_queue_policy": {
709            "required": False, "type": "dict", "default": None,
710            "options": {
711            }
712        }
713    }
714    for attribute_name in module_spec['options']:
715        fields["switch_controller_qos_queue_policy"]['options'][attribute_name] = module_spec['options'][attribute_name]
716        if mkeyname and mkeyname == attribute_name:
717            fields["switch_controller_qos_queue_policy"]['options'][attribute_name]['required'] = True
718
719    check_legacy_fortiosapi()
720    module = AnsibleModule(argument_spec=fields,
721                           supports_check_mode=True)
722
723    versions_check_result = None
724    if module._socket_path:
725        connection = Connection(module._socket_path)
726        if 'access_token' in module.params:
727            connection.set_option('access_token', module.params['access_token'])
728
729        if 'enable_log' in module.params:
730            connection.set_option('enable_log', module.params['enable_log'])
731        else:
732            connection.set_option('enable_log', False)
733        fos = FortiOSHandler(connection, module, mkeyname)
734        versions_check_result = check_schema_versioning(fos, versioned_schema, "switch_controller_qos_queue_policy")
735
736        is_error, has_changed, result = fortios_switch_controller_qos(module.params, fos, module.check_mode)
737
738    else:
739        module.fail_json(**FAIL_SOCKET_MSG)
740
741    if versions_check_result and versions_check_result['matched'] is False:
742        module.warn("Ansible has detected version mismatch between FortOS system and your playbook, see more details by specifying option -vvv")
743
744    if not is_error:
745        if versions_check_result and versions_check_result['matched'] is False:
746            module.exit_json(changed=has_changed, version_check_warning=versions_check_result, meta=result)
747        else:
748            module.exit_json(changed=has_changed, meta=result)
749    else:
750        if versions_check_result and versions_check_result['matched'] is False:
751            module.fail_json(msg="Error in repo", version_check_warning=versions_check_result, meta=result)
752        else:
753            module.fail_json(msg="Error in repo", meta=result)
754
755
756if __name__ == '__main__':
757    main()
758