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