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_vdom_property 27short_description: Configure VDOM property 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 vdom_property 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_vdom_property: 76 description: 77 - Configure VDOM property. 78 default: null 79 type: dict 80 suboptions: 81 custom_service: 82 description: 83 - Maximum guaranteed number of firewall custom services. 84 type: str 85 description: 86 description: 87 - Description. 88 type: str 89 dialup_tunnel: 90 description: 91 - Maximum guaranteed number of dial-up tunnels. 92 type: str 93 firewall_address: 94 description: 95 - Maximum guaranteed number of firewall addresses (IPv4, IPv6, multicast). 96 type: str 97 firewall_addrgrp: 98 description: 99 - Maximum guaranteed number of firewall address groups (IPv4, IPv6). 100 type: str 101 firewall_policy: 102 description: 103 - Maximum guaranteed number of firewall policies (IPv4, IPv6, policy46, policy64, DoS-policy4, DoS-policy6, multicast). 104 type: str 105 ipsec_phase1: 106 description: 107 - Maximum guaranteed number of VPN IPsec phase 1 tunnels. 108 type: str 109 ipsec_phase1_interface: 110 description: 111 - Maximum guaranteed number of VPN IPsec phase1 interface tunnels. 112 type: str 113 ipsec_phase2: 114 description: 115 - Maximum guaranteed number of VPN IPsec phase 2 tunnels. 116 type: str 117 ipsec_phase2_interface: 118 description: 119 - Maximum guaranteed number of VPN IPsec phase2 interface tunnels. 120 type: str 121 log_disk_quota: 122 description: 123 - Log disk quota in MB (range depends on how much disk space is available). 124 type: str 125 name: 126 description: 127 - VDOM name. Source system.vdom.name. 128 required: true 129 type: str 130 onetime_schedule: 131 description: 132 - Maximum guaranteed number of firewall one-time schedules. 133 type: str 134 proxy: 135 description: 136 - Maximum guaranteed number of concurrent proxy users. 137 type: str 138 recurring_schedule: 139 description: 140 - Maximum guaranteed number of firewall recurring schedules. 141 type: str 142 service_group: 143 description: 144 - Maximum guaranteed number of firewall service groups. 145 type: str 146 session: 147 description: 148 - Maximum guaranteed number of sessions. 149 type: str 150 snmp_index: 151 description: 152 - Permanent SNMP Index of the virtual domain (0 - 4294967295). 153 type: int 154 sslvpn: 155 description: 156 - Maximum guaranteed number of SSL-VPNs. 157 type: str 158 user: 159 description: 160 - Maximum guaranteed number of local users. 161 type: str 162 user_group: 163 description: 164 - Maximum guaranteed number of user groups. 165 type: str 166''' 167 168EXAMPLES = ''' 169- hosts: fortigates 170 collections: 171 - fortinet.fortios 172 connection: httpapi 173 vars: 174 vdom: "root" 175 ansible_httpapi_use_ssl: yes 176 ansible_httpapi_validate_certs: no 177 ansible_httpapi_port: 443 178 tasks: 179 - name: Configure VDOM property. 180 fortios_system_vdom_property: 181 vdom: "{{ vdom }}" 182 state: "present" 183 access_token: "<your_own_value>" 184 system_vdom_property: 185 custom_service: "<your_own_value>" 186 description: "<your_own_value>" 187 dialup_tunnel: "<your_own_value>" 188 firewall_address: "<your_own_value>" 189 firewall_addrgrp: "<your_own_value>" 190 firewall_policy: "<your_own_value>" 191 ipsec_phase1: "<your_own_value>" 192 ipsec_phase1_interface: "<your_own_value>" 193 ipsec_phase2: "<your_own_value>" 194 ipsec_phase2_interface: "<your_own_value>" 195 log_disk_quota: "<your_own_value>" 196 name: "default_name_14 (source system.vdom.name)" 197 onetime_schedule: "<your_own_value>" 198 proxy: "<your_own_value>" 199 recurring_schedule: "<your_own_value>" 200 service_group: "<your_own_value>" 201 session: "<your_own_value>" 202 snmp_index: "20" 203 sslvpn: "<your_own_value>" 204 user: "<your_own_value>" 205 user_group: "<your_own_value>" 206 207''' 208 209RETURN = ''' 210build: 211 description: Build number of the fortigate image 212 returned: always 213 type: str 214 sample: '1547' 215http_method: 216 description: Last method used to provision the content into FortiGate 217 returned: always 218 type: str 219 sample: 'PUT' 220http_status: 221 description: Last result given by FortiGate on last operation applied 222 returned: always 223 type: str 224 sample: "200" 225mkey: 226 description: Master key (id) used in the last call to FortiGate 227 returned: success 228 type: str 229 sample: "id" 230name: 231 description: Name of the table used to fulfill the request 232 returned: always 233 type: str 234 sample: "urlfilter" 235path: 236 description: Path of the table used to fulfill the request 237 returned: always 238 type: str 239 sample: "webfilter" 240revision: 241 description: Internal revision number 242 returned: always 243 type: str 244 sample: "17.0.2.10658" 245serial: 246 description: Serial number of the unit 247 returned: always 248 type: str 249 sample: "FGVMEVYYQT3AB5352" 250status: 251 description: Indication of the operation's result 252 returned: always 253 type: str 254 sample: "success" 255vdom: 256 description: Virtual domain used 257 returned: always 258 type: str 259 sample: "root" 260version: 261 description: Version of the FortiGate 262 returned: always 263 type: str 264 sample: "v5.6.3" 265 266''' 267from ansible.module_utils.basic import AnsibleModule 268from ansible.module_utils.connection import Connection 269from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler 270from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi 271from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec 272from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning 273from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG 274from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison 275from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize 276 277 278def filter_system_vdom_property_data(json): 279 option_list = ['custom_service', 'description', 'dialup_tunnel', 280 'firewall_address', 'firewall_addrgrp', 'firewall_policy', 281 'ipsec_phase1', 'ipsec_phase1_interface', 'ipsec_phase2', 282 'ipsec_phase2_interface', 'log_disk_quota', 'name', 283 'onetime_schedule', 'proxy', 'recurring_schedule', 284 'service_group', 'session', 'snmp_index', 285 'sslvpn', 'user', 'user_group'] 286 dictionary = {} 287 288 for attribute in option_list: 289 if attribute in json and json[attribute] is not None: 290 dictionary[attribute] = json[attribute] 291 292 return dictionary 293 294 295def underscore_to_hyphen(data): 296 if isinstance(data, list): 297 for i, elem in enumerate(data): 298 data[i] = underscore_to_hyphen(elem) 299 elif isinstance(data, dict): 300 new_data = {} 301 for k, v in data.items(): 302 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 303 data = new_data 304 305 return data 306 307 308def system_vdom_property(data, fos, check_mode=False): 309 310 vdom = data['vdom'] 311 312 state = data['state'] 313 314 system_vdom_property_data = data['system_vdom_property'] 315 filtered_data = underscore_to_hyphen(filter_system_vdom_property_data(system_vdom_property_data)) 316 317 # check_mode starts from here 318 if check_mode: 319 mkey = fos.get_mkey('system', 'interface', filtered_data, vdom=vdom) 320 current_data = fos.get('system', 'interface', vdom=vdom, mkey=mkey) 321 is_existed = current_data and current_data.get('http_status') == 200 \ 322 and isinstance(current_data.get('results'), list) \ 323 and len(current_data['results']) > 0 324 325 # 2. if it exists and the state is 'present' then compare current settings with desired 326 if state == 'present' or state is True: 327 if mkey is None: 328 return False, True, filtered_data 329 330 # if mkey exists then compare each other 331 # record exits and they're matched or not 332 if is_existed: 333 is_same = is_same_comparison( 334 serialize(current_data['results'][0]), serialize(filtered_data)) 335 return False, not is_same, filtered_data 336 337 # record does not exist 338 return False, True, filtered_data 339 340 if state == 'absent': 341 if mkey is None: 342 return False, False, filtered_data 343 344 if is_existed: 345 return False, True, filtered_data 346 return False, False, filtered_data 347 348 return True, False, {'reason: ': 'Must provide state parameter'} 349 350 if state == "present" or state is True: 351 return fos.set('system', 352 'vdom-property', 353 data=filtered_data, 354 vdom=vdom) 355 356 elif state == "absent": 357 return fos.delete('system', 358 'vdom-property', 359 mkey=filtered_data['name'], 360 vdom=vdom) 361 else: 362 fos._module.fail_json(msg='state must be present or absent!') 363 364 365def is_successful_status(status): 366 return status['status'] == "success" or \ 367 status['http_method'] == "DELETE" and status['http_status'] == 404 368 369 370def fortios_system(data, fos, check_mode): 371 372 if data['system_vdom_property']: 373 resp = system_vdom_property(data, fos, check_mode) 374 else: 375 fos._module.fail_json(msg='missing task body: %s' % ('system_vdom_property')) 376 if check_mode: 377 return resp 378 return not is_successful_status(resp), \ 379 resp['status'] == "success" and \ 380 (resp['revision_changed'] if 'revision_changed' in resp else True), \ 381 resp 382 383 384versioned_schema = { 385 "type": "list", 386 "children": { 387 "service_group": { 388 "type": "string", 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 "firewall_address": { 404 "type": "string", 405 "revisions": { 406 "v6.0.0": True, 407 "v7.0.0": True, 408 "v6.0.5": True, 409 "v6.4.4": True, 410 "v6.4.0": True, 411 "v6.4.1": True, 412 "v6.2.0": True, 413 "v6.2.3": True, 414 "v6.2.5": True, 415 "v6.2.7": True, 416 "v6.0.11": True 417 } 418 }, 419 "firewall_policy": { 420 "type": "string", 421 "revisions": { 422 "v6.0.0": True, 423 "v7.0.0": True, 424 "v6.0.5": True, 425 "v6.4.4": True, 426 "v6.4.0": True, 427 "v6.4.1": True, 428 "v6.2.0": True, 429 "v6.2.3": True, 430 "v6.2.5": True, 431 "v6.2.7": True, 432 "v6.0.11": True 433 } 434 }, 435 "snmp_index": { 436 "type": "integer", 437 "revisions": { 438 "v6.0.0": True, 439 "v7.0.0": True, 440 "v6.0.5": True, 441 "v6.4.4": True, 442 "v6.4.0": True, 443 "v6.4.1": True, 444 "v6.2.0": True, 445 "v6.2.3": True, 446 "v6.2.5": True, 447 "v6.2.7": True, 448 "v6.0.11": True 449 } 450 }, 451 "ipsec_phase1": { 452 "type": "string", 453 "revisions": { 454 "v6.0.0": True, 455 "v7.0.0": True, 456 "v6.0.5": True, 457 "v6.4.4": True, 458 "v6.4.0": True, 459 "v6.4.1": True, 460 "v6.2.0": True, 461 "v6.2.3": True, 462 "v6.2.5": True, 463 "v6.2.7": True, 464 "v6.0.11": True 465 } 466 }, 467 "description": { 468 "type": "string", 469 "revisions": { 470 "v6.0.0": True, 471 "v7.0.0": True, 472 "v6.0.5": True, 473 "v6.4.4": True, 474 "v6.4.0": True, 475 "v6.4.1": True, 476 "v6.2.0": True, 477 "v6.2.3": True, 478 "v6.2.5": True, 479 "v6.2.7": True, 480 "v6.0.11": True 481 } 482 }, 483 "session": { 484 "type": "string", 485 "revisions": { 486 "v6.0.0": True, 487 "v7.0.0": True, 488 "v6.0.5": True, 489 "v6.4.4": True, 490 "v6.4.0": True, 491 "v6.4.1": True, 492 "v6.2.0": True, 493 "v6.2.3": True, 494 "v6.2.5": True, 495 "v6.2.7": True, 496 "v6.0.11": True 497 } 498 }, 499 "ipsec_phase1_interface": { 500 "type": "string", 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 "custom_service": { 516 "type": "string", 517 "revisions": { 518 "v6.0.0": True, 519 "v7.0.0": True, 520 "v6.0.5": True, 521 "v6.4.4": True, 522 "v6.4.0": True, 523 "v6.4.1": True, 524 "v6.2.0": True, 525 "v6.2.3": True, 526 "v6.2.5": True, 527 "v6.2.7": True, 528 "v6.0.11": True 529 } 530 }, 531 "name": { 532 "type": "string", 533 "revisions": { 534 "v6.0.0": True, 535 "v7.0.0": True, 536 "v6.0.5": True, 537 "v6.4.4": True, 538 "v6.4.0": True, 539 "v6.4.1": True, 540 "v6.2.0": True, 541 "v6.2.3": True, 542 "v6.2.5": True, 543 "v6.2.7": True, 544 "v6.0.11": True 545 } 546 }, 547 "ipsec_phase2": { 548 "type": "string", 549 "revisions": { 550 "v6.0.0": True, 551 "v7.0.0": True, 552 "v6.0.5": True, 553 "v6.4.4": True, 554 "v6.4.0": True, 555 "v6.4.1": True, 556 "v6.2.0": True, 557 "v6.2.3": True, 558 "v6.2.5": True, 559 "v6.2.7": True, 560 "v6.0.11": True 561 } 562 }, 563 "ipsec_phase2_interface": { 564 "type": "string", 565 "revisions": { 566 "v6.0.0": True, 567 "v7.0.0": True, 568 "v6.0.5": True, 569 "v6.4.4": True, 570 "v6.4.0": True, 571 "v6.4.1": True, 572 "v6.2.0": True, 573 "v6.2.3": True, 574 "v6.2.5": True, 575 "v6.2.7": True, 576 "v6.0.11": True 577 } 578 }, 579 "dialup_tunnel": { 580 "type": "string", 581 "revisions": { 582 "v6.0.0": True, 583 "v7.0.0": True, 584 "v6.0.5": True, 585 "v6.4.4": True, 586 "v6.4.0": True, 587 "v6.4.1": True, 588 "v6.2.0": True, 589 "v6.2.3": True, 590 "v6.2.5": True, 591 "v6.2.7": True, 592 "v6.0.11": True 593 } 594 }, 595 "log_disk_quota": { 596 "type": "string", 597 "revisions": { 598 "v6.0.0": True, 599 "v7.0.0": True, 600 "v6.0.5": True, 601 "v6.4.4": True, 602 "v6.4.0": True, 603 "v6.4.1": True, 604 "v6.2.0": True, 605 "v6.2.3": True, 606 "v6.2.5": True, 607 "v6.2.7": True, 608 "v6.0.11": True 609 } 610 }, 611 "user_group": { 612 "type": "string", 613 "revisions": { 614 "v6.0.0": True, 615 "v7.0.0": True, 616 "v6.0.5": True, 617 "v6.4.4": True, 618 "v6.4.0": True, 619 "v6.4.1": True, 620 "v6.2.0": True, 621 "v6.2.3": True, 622 "v6.2.5": True, 623 "v6.2.7": True, 624 "v6.0.11": True 625 } 626 }, 627 "sslvpn": { 628 "type": "string", 629 "revisions": { 630 "v6.0.0": True, 631 "v7.0.0": True, 632 "v6.0.5": True, 633 "v6.4.4": True, 634 "v6.4.0": True, 635 "v6.4.1": True, 636 "v6.2.0": True, 637 "v6.2.3": True, 638 "v6.2.5": True, 639 "v6.2.7": True, 640 "v6.0.11": True 641 } 642 }, 643 "onetime_schedule": { 644 "type": "string", 645 "revisions": { 646 "v6.0.0": True, 647 "v7.0.0": True, 648 "v6.0.5": True, 649 "v6.4.4": True, 650 "v6.4.0": True, 651 "v6.4.1": True, 652 "v6.2.0": True, 653 "v6.2.3": True, 654 "v6.2.5": True, 655 "v6.2.7": True, 656 "v6.0.11": True 657 } 658 }, 659 "firewall_addrgrp": { 660 "type": "string", 661 "revisions": { 662 "v6.0.0": True, 663 "v7.0.0": True, 664 "v6.0.5": True, 665 "v6.4.4": True, 666 "v6.4.0": True, 667 "v6.4.1": True, 668 "v6.2.0": True, 669 "v6.2.3": True, 670 "v6.2.5": True, 671 "v6.2.7": True, 672 "v6.0.11": True 673 } 674 }, 675 "proxy": { 676 "type": "string", 677 "revisions": { 678 "v6.0.0": True, 679 "v7.0.0": True, 680 "v6.0.5": True, 681 "v6.4.4": True, 682 "v6.4.0": True, 683 "v6.4.1": True, 684 "v6.2.0": True, 685 "v6.2.3": True, 686 "v6.2.5": True, 687 "v6.2.7": True, 688 "v6.0.11": True 689 } 690 }, 691 "recurring_schedule": { 692 "type": "string", 693 "revisions": { 694 "v6.0.0": True, 695 "v7.0.0": True, 696 "v6.0.5": True, 697 "v6.4.4": True, 698 "v6.4.0": True, 699 "v6.4.1": True, 700 "v6.2.0": True, 701 "v6.2.3": True, 702 "v6.2.5": True, 703 "v6.2.7": True, 704 "v6.0.11": True 705 } 706 }, 707 "user": { 708 "type": "string", 709 "revisions": { 710 "v6.0.0": True, 711 "v7.0.0": True, 712 "v6.0.5": True, 713 "v6.4.4": True, 714 "v6.4.0": True, 715 "v6.4.1": True, 716 "v6.2.0": True, 717 "v6.2.3": True, 718 "v6.2.5": True, 719 "v6.2.7": True, 720 "v6.0.11": True 721 } 722 } 723 }, 724 "revisions": { 725 "v6.0.0": True, 726 "v7.0.0": True, 727 "v6.0.5": True, 728 "v6.4.4": True, 729 "v6.4.0": True, 730 "v6.4.1": True, 731 "v6.2.0": True, 732 "v6.2.3": True, 733 "v6.2.5": True, 734 "v6.2.7": True, 735 "v6.0.11": True 736 } 737} 738 739 740def main(): 741 module_spec = schema_to_module_spec(versioned_schema) 742 mkeyname = 'name' 743 fields = { 744 "access_token": {"required": False, "type": "str", "no_log": True}, 745 "enable_log": {"required": False, "type": bool}, 746 "vdom": {"required": False, "type": "str", "default": "root"}, 747 "state": {"required": True, "type": "str", 748 "choices": ["present", "absent"]}, 749 "system_vdom_property": { 750 "required": False, "type": "dict", "default": None, 751 "options": { 752 } 753 } 754 } 755 for attribute_name in module_spec['options']: 756 fields["system_vdom_property"]['options'][attribute_name] = module_spec['options'][attribute_name] 757 if mkeyname and mkeyname == attribute_name: 758 fields["system_vdom_property"]['options'][attribute_name]['required'] = True 759 760 check_legacy_fortiosapi() 761 module = AnsibleModule(argument_spec=fields, 762 supports_check_mode=True) 763 764 versions_check_result = None 765 if module._socket_path: 766 connection = Connection(module._socket_path) 767 if 'access_token' in module.params: 768 connection.set_option('access_token', module.params['access_token']) 769 770 if 'enable_log' in module.params: 771 connection.set_option('enable_log', module.params['enable_log']) 772 else: 773 connection.set_option('enable_log', False) 774 fos = FortiOSHandler(connection, module, mkeyname) 775 versions_check_result = check_schema_versioning(fos, versioned_schema, "system_vdom_property") 776 777 is_error, has_changed, result = fortios_system(module.params, fos, module.check_mode) 778 779 else: 780 module.fail_json(**FAIL_SOCKET_MSG) 781 782 if versions_check_result and versions_check_result['matched'] is False: 783 module.warn("Ansible has detected version mismatch between FortOS system and your playbook, see more details by specifying option -vvv") 784 785 if not is_error: 786 if versions_check_result and versions_check_result['matched'] is False: 787 module.exit_json(changed=has_changed, version_check_warning=versions_check_result, meta=result) 788 else: 789 module.exit_json(changed=has_changed, meta=result) 790 else: 791 if versions_check_result and versions_check_result['matched'] is False: 792 module.fail_json(msg="Error in repo", version_check_warning=versions_check_result, meta=result) 793 else: 794 module.fail_json(msg="Error in repo", meta=result) 795 796 797if __name__ == '__main__': 798 main() 799