1#!/usr/bin/python 2from __future__ import (absolute_import, division, print_function) 3# Copyright 2019 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_report_layout 27short_description: Report layout configuration 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 report feature and layout category. 31 Examples include all parameters and values need to be adjusted to datasources before usage. 32 Tested with FOS v6.0.5 33version_added: "2.8" 34author: 35 - Miguel Angel Munoz (@mamunozgonzalez) 36 - Nicolas Thomas (@thomnico) 37notes: 38 - Requires fortiosapi library developed by Fortinet 39 - Run as a local_action in your playbook 40requirements: 41 - fortiosapi>=0.9.8 42options: 43 host: 44 description: 45 - FortiOS or FortiGate IP address. 46 type: str 47 required: false 48 username: 49 description: 50 - FortiOS or FortiGate username. 51 type: str 52 required: false 53 password: 54 description: 55 - FortiOS or FortiGate password. 56 type: str 57 default: "" 58 vdom: 59 description: 60 - Virtual domain, among those defined previously. A vdom is a 61 virtual instance of the FortiGate that can be configured and 62 used as a different unit. 63 type: str 64 default: root 65 https: 66 description: 67 - Indicates if the requests towards FortiGate must use HTTPS protocol. 68 type: bool 69 default: true 70 ssl_verify: 71 description: 72 - Ensures FortiGate certificate must be verified by a proper CA. 73 type: bool 74 default: true 75 version_added: 2.9 76 state: 77 description: 78 - Indicates whether to create or remove the object. 79 This attribute was present already in previous version in a deeper level. 80 It has been moved out to this outer level. 81 type: str 82 required: false 83 choices: 84 - present 85 - absent 86 version_added: 2.9 87 report_layout: 88 description: 89 - Report layout configuration. 90 default: null 91 type: dict 92 suboptions: 93 state: 94 description: 95 - B(Deprecated) 96 - Starting with Ansible 2.9 we recommend using the top-level 'state' parameter. 97 - HORIZONTALLINE 98 - Indicates whether to create or remove the object. 99 type: str 100 required: false 101 choices: 102 - present 103 - absent 104 body_item: 105 description: 106 - Configure report body item. 107 type: list 108 suboptions: 109 chart: 110 description: 111 - Report item chart name. 112 type: str 113 chart_options: 114 description: 115 - Report chart options. 116 type: str 117 choices: 118 - include-no-data 119 - hide-title 120 - show-caption 121 column: 122 description: 123 - Report section column number. 124 type: int 125 content: 126 description: 127 - Report item text content. 128 type: str 129 description: 130 description: 131 - Description. 132 type: str 133 drill_down_items: 134 description: 135 - Control how drill down charts are shown. 136 type: str 137 drill_down_types: 138 description: 139 - Control whether keys from the parent being combined or not. 140 type: str 141 hide: 142 description: 143 - Enable/disable hide item in report. 144 type: str 145 choices: 146 - enable 147 - disable 148 id: 149 description: 150 - Report item ID. 151 required: true 152 type: int 153 img_src: 154 description: 155 - Report item image file name. 156 type: str 157 list: 158 description: 159 - Configure report list item. 160 type: list 161 suboptions: 162 content: 163 description: 164 - List entry content. 165 type: str 166 id: 167 description: 168 - List entry ID. 169 required: true 170 type: int 171 list_component: 172 description: 173 - Report item list component. 174 type: str 175 choices: 176 - bullet 177 - numbered 178 misc_component: 179 description: 180 - Report item miscellaneous component. 181 type: str 182 choices: 183 - hline 184 - page-break 185 - column-break 186 - section-start 187 parameters: 188 description: 189 - Parameters. 190 type: list 191 suboptions: 192 id: 193 description: 194 - ID. 195 required: true 196 type: int 197 name: 198 description: 199 - Field name that match field of parameters defined in dataset. 200 type: str 201 value: 202 description: 203 - Value to replace corresponding field of parameters defined in dataset. 204 type: str 205 style: 206 description: 207 - Report item style. 208 type: str 209 table_caption_style: 210 description: 211 - Table chart caption style. 212 type: str 213 table_column_widths: 214 description: 215 - Report item table column widths. 216 type: str 217 table_even_row_style: 218 description: 219 - Table chart even row style. 220 type: str 221 table_head_style: 222 description: 223 - Table chart head style. 224 type: str 225 table_odd_row_style: 226 description: 227 - Table chart odd row style. 228 type: str 229 text_component: 230 description: 231 - Report item text component. 232 type: str 233 choices: 234 - text 235 - heading1 236 - heading2 237 - heading3 238 title: 239 description: 240 - Report section title. 241 type: str 242 top_n: 243 description: 244 - Value of top. 245 type: int 246 type: 247 description: 248 - Report item type. 249 type: str 250 choices: 251 - text 252 - image 253 - chart 254 - misc 255 cutoff_option: 256 description: 257 - Cutoff-option is either run-time or custom. 258 type: str 259 choices: 260 - run-time 261 - custom 262 cutoff_time: 263 description: 264 - "Custom cutoff time to generate report [hh:mm]." 265 type: str 266 day: 267 description: 268 - Schedule days of week to generate report. 269 type: str 270 choices: 271 - sunday 272 - monday 273 - tuesday 274 - wednesday 275 - thursday 276 - friday 277 - saturday 278 description: 279 description: 280 - Description. 281 type: str 282 email_recipients: 283 description: 284 - Email recipients for generated reports. 285 type: str 286 email_send: 287 description: 288 - Enable/disable sending emails after reports are generated. 289 type: str 290 choices: 291 - enable 292 - disable 293 format: 294 description: 295 - Report format. 296 type: str 297 choices: 298 - pdf 299 max_pdf_report: 300 description: 301 - Maximum number of PDF reports to keep at one time (oldest report is overwritten). 302 type: int 303 name: 304 description: 305 - Report layout name. 306 required: true 307 type: str 308 options: 309 description: 310 - Report layout options. 311 type: str 312 choices: 313 - include-table-of-content 314 - auto-numbering-heading 315 - view-chart-as-heading 316 - show-html-navbar-before-heading 317 - dummy-option 318 page: 319 description: 320 - Configure report page. 321 type: dict 322 suboptions: 323 column_break_before: 324 description: 325 - Report page auto column break before heading. 326 type: str 327 choices: 328 - heading1 329 - heading2 330 - heading3 331 footer: 332 description: 333 - Configure report page footer. 334 type: dict 335 suboptions: 336 footer_item: 337 description: 338 - Configure report footer item. 339 type: list 340 suboptions: 341 content: 342 description: 343 - Report item text content. 344 type: str 345 description: 346 description: 347 - Description. 348 type: str 349 id: 350 description: 351 - Report item ID. 352 required: true 353 type: int 354 img_src: 355 description: 356 - Report item image file name. 357 type: str 358 style: 359 description: 360 - Report item style. 361 type: str 362 type: 363 description: 364 - Report item type. 365 type: str 366 choices: 367 - text 368 - image 369 style: 370 description: 371 - Report footer style. 372 type: str 373 header: 374 description: 375 - Configure report page header. 376 type: dict 377 suboptions: 378 header_item: 379 description: 380 - Configure report header item. 381 type: list 382 suboptions: 383 content: 384 description: 385 - Report item text content. 386 type: str 387 description: 388 description: 389 - Description. 390 type: str 391 id: 392 description: 393 - Report item ID. 394 required: true 395 type: int 396 img_src: 397 description: 398 - Report item image file name. 399 type: str 400 style: 401 description: 402 - Report item style. 403 type: str 404 type: 405 description: 406 - Report item type. 407 type: str 408 choices: 409 - text 410 - image 411 style: 412 description: 413 - Report header style. 414 type: str 415 options: 416 description: 417 - Report page options. 418 type: str 419 choices: 420 - header-on-first-page 421 - footer-on-first-page 422 page_break_before: 423 description: 424 - Report page auto page break before heading. 425 type: str 426 choices: 427 - heading1 428 - heading2 429 - heading3 430 paper: 431 description: 432 - Report page paper. 433 type: str 434 choices: 435 - a4 436 - letter 437 schedule_type: 438 description: 439 - Report schedule type. 440 type: str 441 choices: 442 - demand 443 - daily 444 - weekly 445 style_theme: 446 description: 447 - Report style theme. 448 type: str 449 subtitle: 450 description: 451 - Report subtitle. 452 type: str 453 time: 454 description: 455 - "Schedule time to generate report [hh:mm]." 456 type: str 457 title: 458 description: 459 - Report title. 460 type: str 461''' 462 463EXAMPLES = ''' 464- hosts: localhost 465 vars: 466 host: "192.168.122.40" 467 username: "admin" 468 password: "" 469 vdom: "root" 470 ssl_verify: "False" 471 tasks: 472 - name: Report layout configuration. 473 fortios_report_layout: 474 host: "{{ host }}" 475 username: "{{ username }}" 476 password: "{{ password }}" 477 vdom: "{{ vdom }}" 478 https: "False" 479 state: "present" 480 report_layout: 481 body_item: 482 - 483 chart: "<your_own_value>" 484 chart_options: "include-no-data" 485 column: "6" 486 content: "<your_own_value>" 487 description: "<your_own_value>" 488 drill_down_items: "<your_own_value>" 489 drill_down_types: "<your_own_value>" 490 hide: "enable" 491 id: "12" 492 img_src: "<your_own_value>" 493 list: 494 - 495 content: "<your_own_value>" 496 id: "16" 497 list_component: "bullet" 498 misc_component: "hline" 499 parameters: 500 - 501 id: "20" 502 name: "default_name_21" 503 value: "<your_own_value>" 504 style: "<your_own_value>" 505 table_caption_style: "<your_own_value>" 506 table_column_widths: "<your_own_value>" 507 table_even_row_style: "<your_own_value>" 508 table_head_style: "<your_own_value>" 509 table_odd_row_style: "<your_own_value>" 510 text_component: "text" 511 title: "<your_own_value>" 512 top_n: "31" 513 type: "text" 514 cutoff_option: "run-time" 515 cutoff_time: "<your_own_value>" 516 day: "sunday" 517 description: "<your_own_value>" 518 email_recipients: "<your_own_value>" 519 email_send: "enable" 520 format: "pdf" 521 max_pdf_report: "40" 522 name: "default_name_41" 523 options: "include-table-of-content" 524 page: 525 column_break_before: "heading1" 526 footer: 527 footer_item: 528 - 529 content: "<your_own_value>" 530 description: "<your_own_value>" 531 id: "49" 532 img_src: "<your_own_value>" 533 style: "<your_own_value>" 534 type: "text" 535 style: "<your_own_value>" 536 header: 537 header_item: 538 - 539 content: "<your_own_value>" 540 description: "<your_own_value>" 541 id: "58" 542 img_src: "<your_own_value>" 543 style: "<your_own_value>" 544 type: "text" 545 style: "<your_own_value>" 546 options: "header-on-first-page" 547 page_break_before: "heading1" 548 paper: "a4" 549 schedule_type: "demand" 550 style_theme: "<your_own_value>" 551 subtitle: "<your_own_value>" 552 time: "<your_own_value>" 553 title: "<your_own_value>" 554''' 555 556RETURN = ''' 557build: 558 description: Build number of the fortigate image 559 returned: always 560 type: str 561 sample: '1547' 562http_method: 563 description: Last method used to provision the content into FortiGate 564 returned: always 565 type: str 566 sample: 'PUT' 567http_status: 568 description: Last result given by FortiGate on last operation applied 569 returned: always 570 type: str 571 sample: "200" 572mkey: 573 description: Master key (id) used in the last call to FortiGate 574 returned: success 575 type: str 576 sample: "id" 577name: 578 description: Name of the table used to fulfill the request 579 returned: always 580 type: str 581 sample: "urlfilter" 582path: 583 description: Path of the table used to fulfill the request 584 returned: always 585 type: str 586 sample: "webfilter" 587revision: 588 description: Internal revision number 589 returned: always 590 type: str 591 sample: "17.0.2.10658" 592serial: 593 description: Serial number of the unit 594 returned: always 595 type: str 596 sample: "FGVMEVYYQT3AB5352" 597status: 598 description: Indication of the operation's result 599 returned: always 600 type: str 601 sample: "success" 602vdom: 603 description: Virtual domain used 604 returned: always 605 type: str 606 sample: "root" 607version: 608 description: Version of the FortiGate 609 returned: always 610 type: str 611 sample: "v5.6.3" 612 613''' 614 615from ansible.module_utils.basic import AnsibleModule 616from ansible.module_utils.connection import Connection 617from ansible.module_utils.network.fortios.fortios import FortiOSHandler 618from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG 619 620 621def login(data, fos): 622 host = data['host'] 623 username = data['username'] 624 password = data['password'] 625 ssl_verify = data['ssl_verify'] 626 627 fos.debug('on') 628 if 'https' in data and not data['https']: 629 fos.https('off') 630 else: 631 fos.https('on') 632 633 fos.login(host, username, password, verify=ssl_verify) 634 635 636def filter_report_layout_data(json): 637 option_list = ['body_item', 'cutoff_option', 'cutoff_time', 638 'day', 'description', 'email_recipients', 639 'email_send', 'format', 'max_pdf_report', 640 'name', 'options', 'page', 641 'schedule_type', 'style_theme', 'subtitle', 642 'time', 'title'] 643 dictionary = {} 644 645 for attribute in option_list: 646 if attribute in json and json[attribute] is not None: 647 dictionary[attribute] = json[attribute] 648 649 return dictionary 650 651 652def underscore_to_hyphen(data): 653 if isinstance(data, list): 654 for elem in data: 655 elem = underscore_to_hyphen(elem) 656 elif isinstance(data, dict): 657 new_data = {} 658 for k, v in data.items(): 659 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 660 data = new_data 661 662 return data 663 664 665def report_layout(data, fos): 666 vdom = data['vdom'] 667 if 'state' in data and data['state']: 668 state = data['state'] 669 elif 'state' in data['report_layout'] and data['report_layout']: 670 state = data['report_layout']['state'] 671 else: 672 state = True 673 report_layout_data = data['report_layout'] 674 filtered_data = underscore_to_hyphen(filter_report_layout_data(report_layout_data)) 675 676 if state == "present": 677 return fos.set('report', 678 'layout', 679 data=filtered_data, 680 vdom=vdom) 681 682 elif state == "absent": 683 return fos.delete('report', 684 'layout', 685 mkey=filtered_data['name'], 686 vdom=vdom) 687 688 689def is_successful_status(status): 690 return status['status'] == "success" or \ 691 status['http_method'] == "DELETE" and status['http_status'] == 404 692 693 694def fortios_report(data, fos): 695 696 if data['report_layout']: 697 resp = report_layout(data, fos) 698 699 return not is_successful_status(resp), \ 700 resp['status'] == "success", \ 701 resp 702 703 704def main(): 705 fields = { 706 "host": {"required": False, "type": "str"}, 707 "username": {"required": False, "type": "str"}, 708 "password": {"required": False, "type": "str", "default": "", "no_log": True}, 709 "vdom": {"required": False, "type": "str", "default": "root"}, 710 "https": {"required": False, "type": "bool", "default": True}, 711 "ssl_verify": {"required": False, "type": "bool", "default": True}, 712 "state": {"required": False, "type": "str", 713 "choices": ["present", "absent"]}, 714 "report_layout": { 715 "required": False, "type": "dict", "default": None, 716 "options": { 717 "state": {"required": False, "type": "str", 718 "choices": ["present", "absent"]}, 719 "body_item": {"required": False, "type": "list", 720 "options": { 721 "chart": {"required": False, "type": "str"}, 722 "chart_options": {"required": False, "type": "str", 723 "choices": ["include-no-data", "hide-title", "show-caption"]}, 724 "column": {"required": False, "type": "int"}, 725 "content": {"required": False, "type": "str"}, 726 "description": {"required": False, "type": "str"}, 727 "drill_down_items": {"required": False, "type": "str"}, 728 "drill_down_types": {"required": False, "type": "str"}, 729 "hide": {"required": False, "type": "str", 730 "choices": ["enable", "disable"]}, 731 "id": {"required": True, "type": "int"}, 732 "img_src": {"required": False, "type": "str"}, 733 "list": {"required": False, "type": "list", 734 "options": { 735 "content": {"required": False, "type": "str"}, 736 "id": {"required": True, "type": "int"} 737 }}, 738 "list_component": {"required": False, "type": "str", 739 "choices": ["bullet", "numbered"]}, 740 "misc_component": {"required": False, "type": "str", 741 "choices": ["hline", "page-break", "column-break", 742 "section-start"]}, 743 "parameters": {"required": False, "type": "list", 744 "options": { 745 "id": {"required": True, "type": "int"}, 746 "name": {"required": False, "type": "str"}, 747 "value": {"required": False, "type": "str"} 748 }}, 749 "style": {"required": False, "type": "str"}, 750 "table_caption_style": {"required": False, "type": "str"}, 751 "table_column_widths": {"required": False, "type": "str"}, 752 "table_even_row_style": {"required": False, "type": "str"}, 753 "table_head_style": {"required": False, "type": "str"}, 754 "table_odd_row_style": {"required": False, "type": "str"}, 755 "text_component": {"required": False, "type": "str", 756 "choices": ["text", "heading1", "heading2", 757 "heading3"]}, 758 "title": {"required": False, "type": "str"}, 759 "top_n": {"required": False, "type": "int"}, 760 "type": {"required": False, "type": "str", 761 "choices": ["text", "image", "chart", 762 "misc"]} 763 }}, 764 "cutoff_option": {"required": False, "type": "str", 765 "choices": ["run-time", "custom"]}, 766 "cutoff_time": {"required": False, "type": "str"}, 767 "day": {"required": False, "type": "str", 768 "choices": ["sunday", "monday", "tuesday", 769 "wednesday", "thursday", "friday", 770 "saturday"]}, 771 "description": {"required": False, "type": "str"}, 772 "email_recipients": {"required": False, "type": "str"}, 773 "email_send": {"required": False, "type": "str", 774 "choices": ["enable", "disable"]}, 775 "format": {"required": False, "type": "str", 776 "choices": ["pdf"]}, 777 "max_pdf_report": {"required": False, "type": "int"}, 778 "name": {"required": True, "type": "str"}, 779 "options": {"required": False, "type": "str", 780 "choices": ["include-table-of-content", "auto-numbering-heading", "view-chart-as-heading", 781 "show-html-navbar-before-heading", "dummy-option"]}, 782 "page": {"required": False, "type": "dict", 783 "options": { 784 "column_break_before": {"required": False, "type": "str", 785 "choices": ["heading1", "heading2", "heading3"]}, 786 "footer": {"required": False, "type": "dict", 787 "options": { 788 "footer_item": {"required": False, "type": "list", 789 "options": { 790 "content": {"required": False, "type": "str"}, 791 "description": {"required": False, "type": "str"}, 792 "id": {"required": True, "type": "int"}, 793 "img_src": {"required": False, "type": "str"}, 794 "style": {"required": False, "type": "str"}, 795 "type": {"required": False, "type": "str", 796 "choices": ["text", "image"]} 797 }}, 798 "style": {"required": False, "type": "str"} 799 }}, 800 "header": {"required": False, "type": "dict", 801 "options": { 802 "header_item": {"required": False, "type": "list", 803 "options": { 804 "content": {"required": False, "type": "str"}, 805 "description": {"required": False, "type": "str"}, 806 "id": {"required": True, "type": "int"}, 807 "img_src": {"required": False, "type": "str"}, 808 "style": {"required": False, "type": "str"}, 809 "type": {"required": False, "type": "str", 810 "choices": ["text", "image"]} 811 }}, 812 "style": {"required": False, "type": "str"} 813 }}, 814 "options": {"required": False, "type": "str", 815 "choices": ["header-on-first-page", "footer-on-first-page"]}, 816 "page_break_before": {"required": False, "type": "str", 817 "choices": ["heading1", "heading2", "heading3"]}, 818 "paper": {"required": False, "type": "str", 819 "choices": ["a4", "letter"]} 820 }}, 821 "schedule_type": {"required": False, "type": "str", 822 "choices": ["demand", "daily", "weekly"]}, 823 "style_theme": {"required": False, "type": "str"}, 824 "subtitle": {"required": False, "type": "str"}, 825 "time": {"required": False, "type": "str"}, 826 "title": {"required": False, "type": "str"} 827 828 } 829 } 830 } 831 832 module = AnsibleModule(argument_spec=fields, 833 supports_check_mode=False) 834 835 # legacy_mode refers to using fortiosapi instead of HTTPAPI 836 legacy_mode = 'host' in module.params and module.params['host'] is not None and \ 837 'username' in module.params and module.params['username'] is not None and \ 838 'password' in module.params and module.params['password'] is not None 839 840 if not legacy_mode: 841 if module._socket_path: 842 connection = Connection(module._socket_path) 843 fos = FortiOSHandler(connection) 844 845 is_error, has_changed, result = fortios_report(module.params, fos) 846 else: 847 module.fail_json(**FAIL_SOCKET_MSG) 848 else: 849 try: 850 from fortiosapi import FortiOSAPI 851 except ImportError: 852 module.fail_json(msg="fortiosapi module is required") 853 854 fos = FortiOSAPI() 855 856 login(module.params, fos) 857 is_error, has_changed, result = fortios_report(module.params, fos) 858 fos.logout() 859 860 if not is_error: 861 module.exit_json(changed=has_changed, meta=result) 862 else: 863 module.fail_json(msg="Error in repo", meta=result) 864 865 866if __name__ == '__main__': 867 main() 868