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