From 69fcd771e047a7ea237713f17265e6ec08b9668d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Angel=20Mu=C3=B1oz=20Gonz=C3=A1lez?= Date: Mon, 26 Aug 2019 15:25:53 +0200 Subject: [PATCH] FortiOS modules for 2.9 - 8 (#61308) * FortiOS modules for 2.9 - 8 * Add missing files * Fix trailing spaces --- .../network/fortios/fortios_report_layout.py | 378 ++-- .../network/fortios/fortios_report_setting.py | 147 +- .../network/fortios/fortios_report_style.py | 310 ++-- .../network/fortios/fortios_report_theme.py | 364 ++-- .../fortios/fortios_router_access_list.py | 155 +- .../fortios/fortios_router_auth_path.py | 143 +- .../network/fortios/fortios_router_bfd.py | 117 +- .../network/fortios/fortios_router_bfd6.py | 124 +- .../network/fortios/fortios_router_bgp.py | 1652 ++++++++++------- .../fortios/fortios_router_multicast.py | 544 +++--- .../fortios/fortios_router_multicast6.py | 181 +- .../fortios/fortios_router_multicast_flow.py | 158 +- .../network/fortios/fortios_router_ospf.py | 577 +++--- .../network/fortios/fortios_router_ospf6.py | 421 +++-- .../network/fortios/fortios_router_policy.py | 238 ++- .../network/fortios/fortios_router_policy6.py | 200 +- .../fortios/fortios_router_prefix_list.py | 149 +- .../network/fortios/fortios_router_rip.py | 294 +-- .../network/fortios/fortios_router_setting.py | 124 +- .../network/fortios/fortios_router_static.py | 204 +- test/sanity/ignore.txt | 37 - .../fortios/test_fortios_report_layout.py | 329 ++++ .../fortios/test_fortios_report_setting.py | 183 ++ .../fortios/test_fortios_report_style.py | 449 +++++ .../fortios/test_fortios_report_theme.py | 489 +++++ .../test_fortios_router_access_list.py | 219 +++ .../fortios/test_fortios_router_auth_path.py | 219 +++ .../fortios/test_fortios_router_bfd.py | 143 ++ .../fortios/test_fortios_router_bfd6.py | 143 ++ .../fortios/test_fortios_router_bgp.py | 447 +++++ .../fortios/test_fortios_router_multicast.py | 159 ++ .../fortios/test_fortios_router_multicast6.py | 159 ++ .../test_fortios_router_multicast_flow.py | 209 +++ .../fortios/test_fortios_router_ospf.py | 335 ++++ .../fortios/test_fortios_router_ospf6.py | 239 +++ .../fortios/test_fortios_router_policy.py | 339 ++++ .../fortios/test_fortios_router_policy6.py | 319 ++++ .../test_fortios_router_prefix_list.py | 219 +++ .../fortios/test_fortios_router_rip.py | 207 +++ .../fortios/test_fortios_router_setting.py | 159 ++ .../fortios/test_fortios_router_static.py | 379 ++++ 41 files changed, 9308 insertions(+), 2553 deletions(-) create mode 100644 test/units/modules/network/fortios/test_fortios_report_layout.py create mode 100644 test/units/modules/network/fortios/test_fortios_report_setting.py create mode 100644 test/units/modules/network/fortios/test_fortios_report_style.py create mode 100644 test/units/modules/network/fortios/test_fortios_report_theme.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_access_list.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_auth_path.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_bfd.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_bfd6.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_bgp.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_multicast.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_multicast6.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_multicast_flow.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_ospf.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_ospf6.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_policy.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_policy6.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_prefix_list.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_rip.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_setting.py create mode 100644 test/units/modules/network/fortios/test_fortios_router_static.py diff --git a/lib/ansible/modules/network/fortios/fortios_report_layout.py b/lib/ansible/modules/network/fortios/fortios_report_layout.py index b28f89410ad..1f5ee774865 100644 --- a/lib/ansible/modules/network/fortios/fortios_report_layout.py +++ b/lib/ansible/modules/network/fortios/fortios_report_layout.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_report_layout short_description: Report layout configuration in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify report feature and layout category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,50 +41,66 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 report_layout: description: - Report layout configuration. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent - body-item: + body_item: description: - Configure report body item. + type: list suboptions: chart: description: - Report item chart name. - chart-options: + type: str + chart_options: description: - Report chart options. + type: str choices: - include-no-data - hide-title @@ -95,21 +108,27 @@ options: column: description: - Report section column number. + type: int content: description: - Report item text content. + type: str description: description: - Description. - drill-down-items: + type: str + drill_down_items: description: - Control how drill down charts are shown. - drill-down-types: + type: str + drill_down_types: description: - Control whether keys from the parent being combined or not. + type: str hide: description: - Enable/disable hide item in report. + type: str choices: - enable - disable @@ -117,29 +136,36 @@ options: description: - Report item ID. required: true - img-src: + type: int + img_src: description: - Report item image file name. + type: str list: description: - Configure report list item. + type: list suboptions: content: description: - List entry content. + type: str id: description: - List entry ID. required: true - list-component: + type: int + list_component: description: - Report item list component. + type: str choices: - bullet - numbered - misc-component: + misc_component: description: - Report item miscellaneous component. + type: str choices: - hline - page-break @@ -148,38 +174,49 @@ options: parameters: description: - Parameters. + type: list suboptions: id: description: - ID. required: true + type: int name: description: - Field name that match field of parameters defined in dataset. + type: str value: description: - Value to replace corresponding field of parameters defined in dataset. + type: str style: description: - Report item style. - table-caption-style: + type: str + table_caption_style: description: - Table chart caption style. - table-column-widths: + type: str + table_column_widths: description: - Report item table column widths. - table-even-row-style: + type: str + table_even_row_style: description: - Table chart even row style. - table-head-style: + type: str + table_head_style: description: - Table chart head style. - table-odd-row-style: + type: str + table_odd_row_style: description: - Table chart odd row style. - text-component: + type: str + text_component: description: - Report item text component. + type: str choices: - text - heading1 @@ -188,29 +225,35 @@ options: title: description: - Report section title. - top-n: + type: str + top_n: description: - Value of top. + type: int type: description: - Report item type. + type: str choices: - text - image - chart - misc - cutoff-option: + cutoff_option: description: - Cutoff-option is either run-time or custom. + type: str choices: - run-time - custom - cutoff-time: + cutoff_time: description: - "Custom cutoff time to generate report [hh:mm]." + type: str day: description: - Schedule days of week to generate report. + type: str choices: - sunday - monday @@ -222,30 +265,37 @@ options: description: description: - Description. - email-recipients: + type: str + email_recipients: description: - Email recipients for generated reports. - email-send: + type: str + email_send: description: - Enable/disable sending emails after reports are generated. + type: str choices: - enable - disable format: description: - Report format. + type: str choices: - pdf - max-pdf-report: + max_pdf_report: description: - Maximum number of PDF reports to keep at one time (oldest report is overwritten). + type: int name: description: - Report layout name. required: true + type: str options: description: - Report layout options. + type: str choices: - include-table-of-content - auto-numbering-heading @@ -255,10 +305,12 @@ options: page: description: - Configure report page. + type: dict suboptions: - column-break-before: + column_break_before: description: - Report page auto column break before heading. + type: str choices: - heading1 - heading2 @@ -266,78 +318,98 @@ options: footer: description: - Configure report page footer. + type: dict suboptions: - footer-item: + footer_item: description: - Configure report footer item. + type: list suboptions: content: description: - Report item text content. + type: str description: description: - Description. + type: str id: description: - Report item ID. required: true - img-src: + type: int + img_src: description: - Report item image file name. + type: str style: description: - Report item style. + type: str type: description: - Report item type. + type: str choices: - text - image style: description: - Report footer style. + type: str header: description: - Configure report page header. + type: dict suboptions: - header-item: + header_item: description: - Configure report header item. + type: list suboptions: content: description: - Report item text content. + type: str description: description: - Description. + type: str id: description: - Report item ID. required: true - img-src: + type: int + img_src: description: - Report item image file name. + type: str style: description: - Report item style. + type: str type: description: - Report item type. + type: str choices: - text - image style: description: - Report header style. + type: str options: description: - Report page options. + type: str choices: - header-on-first-page - footer-on-first-page - page-break-before: + page_break_before: description: - Report page auto page break before heading. + type: str choices: - heading1 - heading2 @@ -345,28 +417,34 @@ options: paper: description: - Report page paper. + type: str choices: - a4 - letter - schedule-type: + schedule_type: description: - Report schedule type. + type: str choices: - demand - daily - weekly - style-theme: + style_theme: description: - Report style theme. + type: str subtitle: description: - Report subtitle. + type: str time: description: - "Schedule time to generate report [hh:mm]." + type: str title: description: - Report title. + type: str ''' EXAMPLES = ''' @@ -376,6 +454,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Report layout configuration. fortios_report_layout: @@ -384,78 +463,78 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" report_layout: - state: "present" - body-item: + body_item: - chart: "" - chart-options: "include-no-data" + chart_options: "include-no-data" column: "6" content: "" description: "" - drill-down-items: "" - drill-down-types: "" + drill_down_items: "" + drill_down_types: "" hide: "enable" id: "12" - img-src: "" + img_src: "" list: - content: "" id: "16" - list-component: "bullet" - misc-component: "hline" + list_component: "bullet" + misc_component: "hline" parameters: - id: "20" name: "default_name_21" value: "" style: "" - table-caption-style: "" - table-column-widths: "" - table-even-row-style: "" - table-head-style: "" - table-odd-row-style: "" - text-component: "text" + table_caption_style: "" + table_column_widths: "" + table_even_row_style: "" + table_head_style: "" + table_odd_row_style: "" + text_component: "text" title: "" - top-n: "31" + top_n: "31" type: "text" - cutoff-option: "run-time" - cutoff-time: "" + cutoff_option: "run-time" + cutoff_time: "" day: "sunday" description: "" - email-recipients: "" - email-send: "enable" + email_recipients: "" + email_send: "enable" format: "pdf" - max-pdf-report: "40" + max_pdf_report: "40" name: "default_name_41" options: "include-table-of-content" page: - column-break-before: "heading1" + column_break_before: "heading1" footer: - footer-item: + footer_item: - content: "" description: "" id: "49" - img-src: "" + img_src: "" style: "" type: "text" style: "" header: - header-item: + header_item: - content: "" description: "" id: "58" - img-src: "" + img_src: "" style: "" type: "text" style: "" options: "header-on-first-page" - page-break-before: "heading1" + page_break_before: "heading1" paper: "a4" - schedule-type: "demand" - style-theme: "" + schedule_type: "demand" + style_theme: "" subtitle: "" time: "" title: "" @@ -521,14 +600,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -536,15 +617,15 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_report_layout_data(json): - option_list = ['body-item', 'cutoff-option', 'cutoff-time', - 'day', 'description', 'email-recipients', - 'email-send', 'format', 'max-pdf-report', + option_list = ['body_item', 'cutoff_option', 'cutoff_time', + 'day', 'description', 'email_recipients', + 'email_send', 'format', 'max_pdf_report', 'name', 'options', 'page', - 'schedule-type', 'style-theme', 'subtitle', + 'schedule_type', 'style_theme', 'subtitle', 'time', 'title'] dictionary = {} @@ -555,83 +636,88 @@ def filter_report_layout_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data def report_layout(data, fos): vdom = data['vdom'] + state = data['state'] report_layout_data = data['report_layout'] - flattened_data = flatten_multilists_attributes(report_layout_data) - filtered_data = filter_report_layout_data(flattened_data) - if report_layout_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_report_layout_data(report_layout_data)) + + if state == "present": return fos.set('report', 'layout', data=filtered_data, vdom=vdom) - elif report_layout_data['state'] == "absent": + elif state == "absent": return fos.delete('report', 'layout', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_report(data, fos): - login(data) if data['report_layout']: resp = report_layout(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "report_layout": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, - "body-item": {"required": False, "type": "list", + "body_item": {"required": False, "type": "list", "options": { "chart": {"required": False, "type": "str"}, - "chart-options": {"required": False, "type": "str", + "chart_options": {"required": False, "type": "str", "choices": ["include-no-data", "hide-title", "show-caption"]}, "column": {"required": False, "type": "int"}, "content": {"required": False, "type": "str"}, "description": {"required": False, "type": "str"}, - "drill-down-items": {"required": False, "type": "str"}, - "drill-down-types": {"required": False, "type": "str"}, + "drill_down_items": {"required": False, "type": "str"}, + "drill_down_types": {"required": False, "type": "str"}, "hide": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "id": {"required": True, "type": "int"}, - "img-src": {"required": False, "type": "str"}, + "img_src": {"required": False, "type": "str"}, "list": {"required": False, "type": "list", "options": { "content": {"required": False, "type": "str"}, "id": {"required": True, "type": "int"} }}, - "list-component": {"required": False, "type": "str", + "list_component": {"required": False, "type": "str", "choices": ["bullet", "numbered"]}, - "misc-component": {"required": False, "type": "str", + "misc_component": {"required": False, "type": "str", "choices": ["hline", "page-break", "column-break", "section-start"]}, "parameters": {"required": False, "type": "list", @@ -641,50 +727,50 @@ def main(): "value": {"required": False, "type": "str"} }}, "style": {"required": False, "type": "str"}, - "table-caption-style": {"required": False, "type": "str"}, - "table-column-widths": {"required": False, "type": "str"}, - "table-even-row-style": {"required": False, "type": "str"}, - "table-head-style": {"required": False, "type": "str"}, - "table-odd-row-style": {"required": False, "type": "str"}, - "text-component": {"required": False, "type": "str", + "table_caption_style": {"required": False, "type": "str"}, + "table_column_widths": {"required": False, "type": "str"}, + "table_even_row_style": {"required": False, "type": "str"}, + "table_head_style": {"required": False, "type": "str"}, + "table_odd_row_style": {"required": False, "type": "str"}, + "text_component": {"required": False, "type": "str", "choices": ["text", "heading1", "heading2", "heading3"]}, "title": {"required": False, "type": "str"}, - "top-n": {"required": False, "type": "int"}, + "top_n": {"required": False, "type": "int"}, "type": {"required": False, "type": "str", "choices": ["text", "image", "chart", "misc"]} }}, - "cutoff-option": {"required": False, "type": "str", + "cutoff_option": {"required": False, "type": "str", "choices": ["run-time", "custom"]}, - "cutoff-time": {"required": False, "type": "str"}, + "cutoff_time": {"required": False, "type": "str"}, "day": {"required": False, "type": "str", "choices": ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"]}, "description": {"required": False, "type": "str"}, - "email-recipients": {"required": False, "type": "str"}, - "email-send": {"required": False, "type": "str", + "email_recipients": {"required": False, "type": "str"}, + "email_send": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "format": {"required": False, "type": "str", "choices": ["pdf"]}, - "max-pdf-report": {"required": False, "type": "int"}, + "max_pdf_report": {"required": False, "type": "int"}, "name": {"required": True, "type": "str"}, "options": {"required": False, "type": "str", "choices": ["include-table-of-content", "auto-numbering-heading", "view-chart-as-heading", "show-html-navbar-before-heading", "dummy-option"]}, "page": {"required": False, "type": "dict", "options": { - "column-break-before": {"required": False, "type": "str", + "column_break_before": {"required": False, "type": "str", "choices": ["heading1", "heading2", "heading3"]}, "footer": {"required": False, "type": "dict", "options": { - "footer-item": {"required": False, "type": "list", + "footer_item": {"required": False, "type": "list", "options": { "content": {"required": False, "type": "str"}, "description": {"required": False, "type": "str"}, "id": {"required": True, "type": "int"}, - "img-src": {"required": False, "type": "str"}, + "img_src": {"required": False, "type": "str"}, "style": {"required": False, "type": "str"}, "type": {"required": False, "type": "str", "choices": ["text", "image"]} @@ -693,12 +779,12 @@ def main(): }}, "header": {"required": False, "type": "dict", "options": { - "header-item": {"required": False, "type": "list", + "header_item": {"required": False, "type": "list", "options": { "content": {"required": False, "type": "str"}, "description": {"required": False, "type": "str"}, "id": {"required": True, "type": "int"}, - "img-src": {"required": False, "type": "str"}, + "img_src": {"required": False, "type": "str"}, "style": {"required": False, "type": "str"}, "type": {"required": False, "type": "str", "choices": ["text", "image"]} @@ -707,14 +793,14 @@ def main(): }}, "options": {"required": False, "type": "str", "choices": ["header-on-first-page", "footer-on-first-page"]}, - "page-break-before": {"required": False, "type": "str", + "page_break_before": {"required": False, "type": "str", "choices": ["heading1", "heading2", "heading3"]}, "paper": {"required": False, "type": "str", "choices": ["a4", "letter"]} }}, - "schedule-type": {"required": False, "type": "str", + "schedule_type": {"required": False, "type": "str", "choices": ["demand", "daily", "weekly"]}, - "style-theme": {"required": False, "type": "str"}, + "style_theme": {"required": False, "type": "str"}, "subtitle": {"required": False, "type": "str"}, "time": {"required": False, "type": "str"}, "title": {"required": False, "type": "str"} @@ -725,15 +811,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_report(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_report(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_report(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_report_setting.py b/lib/ansible/modules/network/fortios/fortios_report_setting.py index a98dcffaeb6..57fbf314362 100644 --- a/lib/ansible/modules/network/fortios/fortios_report_setting.py +++ b/lib/ansible/modules/network/fortios/fortios_report_setting.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_report_setting short_description: Report setting configuration in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify report feature and setting category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,59 +41,74 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 report_setting: description: - Report setting configuration. default: null + type: dict suboptions: fortiview: description: - Enable/disable historical FortiView. + type: str choices: - enable - disable - pdf-report: + pdf_report: description: - Enable/disable PDF report. + type: str choices: - enable - disable - report-source: + report_source: description: - Report log source. + type: str choices: - forward-traffic - sniffer-traffic - local-deny-traffic - top-n: + top_n: description: - Number of items to populate (100 - 4000). - web-browsing-threshold: + type: int + web_browsing_threshold: description: - Web browsing time calculation threshold (3 - 15 min). + type: int ''' EXAMPLES = ''' @@ -106,6 +118,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Report setting configuration. fortios_report_setting: @@ -116,10 +129,10 @@ EXAMPLES = ''' https: "False" report_setting: fortiview: "enable" - pdf-report: "enable" - report-source: "forward-traffic" - top-n: "6" - web-browsing-threshold: "7" + pdf_report: "enable" + report_source: "forward-traffic" + top_n: "6" + web_browsing_threshold: "7" ''' RETURN = ''' @@ -182,14 +195,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -197,12 +212,12 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_report_setting_data(json): - option_list = ['fortiview', 'pdf-report', 'report-source', - 'top-n', 'web-browsing-threshold'] + option_list = ['fortiview', 'pdf_report', 'report_source', + 'top_n', 'web_browsing_threshold'] dictionary = {} for attribute in option_list: @@ -212,17 +227,15 @@ def filter_report_setting_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data @@ -230,42 +243,48 @@ def flatten_multilists_attributes(data): def report_setting(data, fos): vdom = data['vdom'] report_setting_data = data['report_setting'] - flattened_data = flatten_multilists_attributes(report_setting_data) - filtered_data = filter_report_setting_data(flattened_data) + filtered_data = underscore_to_hyphen(filter_report_setting_data(report_setting_data)) + return fos.set('report', 'setting', data=filtered_data, vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_report(data, fos): - login(data) if data['report_setting']: resp = report_setting(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, "report_setting": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { "fortiview": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "pdf-report": {"required": False, "type": "str", + "pdf_report": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "report-source": {"required": False, "type": "str", + "report_source": {"required": False, "type": "str", "choices": ["forward-traffic", "sniffer-traffic", "local-deny-traffic"]}, - "top-n": {"required": False, "type": "int"}, - "web-browsing-threshold": {"required": False, "type": "int"} + "top_n": {"required": False, "type": "int"}, + "web_browsing_threshold": {"required": False, "type": "int"} } } @@ -273,15 +292,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_report(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_report(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_report(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_report_style.py b/lib/ansible/modules/network/fortios/fortios_report_style.py index 952a6687e6d..34974032918 100644 --- a/lib/ansible/modules/network/fortios/fortios_report_style.py +++ b/lib/ansible/modules/network/fortios/fortios_report_style.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_report_style short_description: Report style configuration in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify report feature and style category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,124 +41,158 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 report_style: description: - Report style configuration. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent align: description: - Alignment. + type: str choices: - left - center - right - justify - bg-color: + bg_color: description: - Background color. - border-bottom: + type: str + border_bottom: description: - Border bottom. - border-left: + type: str + border_left: description: - Border left. - border-right: + type: str + border_right: description: - Border right. - border-top: + type: str + border_top: description: - Border top. - column-gap: + type: str + column_gap: description: - Column gap. - column-span: + type: str + column_span: description: - Column span. + type: str choices: - none - all - fg-color: + fg_color: description: - Foreground color. - font-family: + type: str + font_family: description: - Font family. + type: str choices: - Verdana - Arial - Helvetica - Courier - Times - font-size: + font_size: description: - Font size. - font-style: + type: str + font_style: description: - Font style. + type: str choices: - normal - italic - font-weight: + font_weight: description: - Font weight. + type: str choices: - normal - bold height: description: - Height. - line-height: + type: str + line_height: description: - Text line height. - margin-bottom: + type: str + margin_bottom: description: - Margin bottom. - margin-left: + type: str + margin_left: description: - Margin left. - margin-right: + type: str + margin_right: description: - Margin right. - margin-top: + type: str + margin_top: description: - Margin top. + type: str name: description: - Report style name. required: true + type: str options: description: - Report style options. + type: str choices: - font - text @@ -172,21 +203,26 @@ options: - border - padding - column - padding-bottom: + padding_bottom: description: - Padding bottom. - padding-left: + type: str + padding_left: description: - Padding left. - padding-right: + type: str + padding_right: description: - Padding right. - padding-top: + type: str + padding_top: description: - Padding top. + type: str width: description: - Width. + type: str ''' EXAMPLES = ''' @@ -196,6 +232,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Report style configuration. fortios_report_style: @@ -204,33 +241,33 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" report_style: - state: "present" align: "left" - bg-color: "" - border-bottom: "" - border-left: "" - border-right: "" - border-top: "" - column-gap: "" - column-span: "none" - fg-color: "" - font-family: "Verdana" - font-size: "" - font-style: "normal" - font-weight: "normal" + bg_color: "" + border_bottom: "" + border_left: "" + border_right: "" + border_top: "" + column_gap: "" + column_span: "none" + fg_color: "" + font_family: "Verdana" + font_size: "" + font_style: "normal" + font_weight: "normal" height: "" - line-height: "" - margin-bottom: "" - margin-left: "" - margin-right: "" - margin-top: "" + line_height: "" + margin_bottom: "" + margin_left: "" + margin_right: "" + margin_top: "" name: "default_name_22" options: "font" - padding-bottom: "" - padding-left: "" - padding-right: "" - padding-top: "" + padding_bottom: "" + padding_left: "" + padding_right: "" + padding_top: "" width: "" ''' @@ -294,14 +331,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -309,19 +348,19 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_report_style_data(json): - option_list = ['align', 'bg-color', 'border-bottom', - 'border-left', 'border-right', 'border-top', - 'column-gap', 'column-span', 'fg-color', - 'font-family', 'font-size', 'font-style', - 'font-weight', 'height', 'line-height', - 'margin-bottom', 'margin-left', 'margin-right', - 'margin-top', 'name', 'options', - 'padding-bottom', 'padding-left', 'padding-right', - 'padding-top', 'width'] + option_list = ['align', 'bg_color', 'border_bottom', + 'border_left', 'border_right', 'border_top', + 'column_gap', 'column_span', 'fg_color', + 'font_family', 'font_size', 'font_style', + 'font_weight', 'height', 'line_height', + 'margin_bottom', 'margin_left', 'margin_right', + 'margin_top', 'name', 'options', + 'padding_bottom', 'padding_left', 'padding_right', + 'padding_top', 'width'] dictionary = {} for attribute in option_list: @@ -331,96 +370,101 @@ def filter_report_style_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data def report_style(data, fos): vdom = data['vdom'] + state = data['state'] report_style_data = data['report_style'] - flattened_data = flatten_multilists_attributes(report_style_data) - filtered_data = filter_report_style_data(flattened_data) - if report_style_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_report_style_data(report_style_data)) + + if state == "present": return fos.set('report', 'style', data=filtered_data, vdom=vdom) - elif report_style_data['state'] == "absent": + elif state == "absent": return fos.delete('report', 'style', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_report(data, fos): - login(data) if data['report_style']: resp = report_style(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "report_style": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "align": {"required": False, "type": "str", "choices": ["left", "center", "right", "justify"]}, - "bg-color": {"required": False, "type": "str"}, - "border-bottom": {"required": False, "type": "str"}, - "border-left": {"required": False, "type": "str"}, - "border-right": {"required": False, "type": "str"}, - "border-top": {"required": False, "type": "str"}, - "column-gap": {"required": False, "type": "str"}, - "column-span": {"required": False, "type": "str", + "bg_color": {"required": False, "type": "str"}, + "border_bottom": {"required": False, "type": "str"}, + "border_left": {"required": False, "type": "str"}, + "border_right": {"required": False, "type": "str"}, + "border_top": {"required": False, "type": "str"}, + "column_gap": {"required": False, "type": "str"}, + "column_span": {"required": False, "type": "str", "choices": ["none", "all"]}, - "fg-color": {"required": False, "type": "str"}, - "font-family": {"required": False, "type": "str", + "fg_color": {"required": False, "type": "str"}, + "font_family": {"required": False, "type": "str", "choices": ["Verdana", "Arial", "Helvetica", "Courier", "Times"]}, - "font-size": {"required": False, "type": "str"}, - "font-style": {"required": False, "type": "str", + "font_size": {"required": False, "type": "str"}, + "font_style": {"required": False, "type": "str", "choices": ["normal", "italic"]}, - "font-weight": {"required": False, "type": "str", + "font_weight": {"required": False, "type": "str", "choices": ["normal", "bold"]}, "height": {"required": False, "type": "str"}, - "line-height": {"required": False, "type": "str"}, - "margin-bottom": {"required": False, "type": "str"}, - "margin-left": {"required": False, "type": "str"}, - "margin-right": {"required": False, "type": "str"}, - "margin-top": {"required": False, "type": "str"}, + "line_height": {"required": False, "type": "str"}, + "margin_bottom": {"required": False, "type": "str"}, + "margin_left": {"required": False, "type": "str"}, + "margin_right": {"required": False, "type": "str"}, + "margin_top": {"required": False, "type": "str"}, "name": {"required": True, "type": "str"}, "options": {"required": False, "type": "str", "choices": ["font", "text", "color", "align", "size", "margin", "border", "padding", "column"]}, - "padding-bottom": {"required": False, "type": "str"}, - "padding-left": {"required": False, "type": "str"}, - "padding-right": {"required": False, "type": "str"}, - "padding-top": {"required": False, "type": "str"}, + "padding_bottom": {"required": False, "type": "str"}, + "padding_left": {"required": False, "type": "str"}, + "padding_right": {"required": False, "type": "str"}, + "padding_top": {"required": False, "type": "str"}, "width": {"required": False, "type": "str"} } @@ -429,15 +473,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_report(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_report(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_report(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_report_theme.py b/lib/ansible/modules/network/fortios/fortios_report_theme.py index d88f18d2b27..0928b98e920 100644 --- a/lib/ansible/modules/network/fortios/fortios_report_theme.py +++ b/lib/ansible/modules/network/fortios/fortios_report_theme.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_report_theme short_description: Report themes configuratio in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify report feature and theme category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,138 +41,181 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 report_theme: description: - Report themes configuration default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent - bullet-list-style: + bullet_list_style: description: - Bullet list style. - column-count: + type: str + column_count: description: - Report page column count. + type: str choices: - 1 - 2 - 3 - default-html-style: + default_html_style: description: - Default HTML report style. - default-pdf-style: + type: str + default_pdf_style: description: - Default PDF report style. - graph-chart-style: + type: str + graph_chart_style: description: - Graph chart style. - heading1-style: + type: str + heading1_style: description: - Report heading style. - heading2-style: + type: str + heading2_style: description: - Report heading style. - heading3-style: + type: str + heading3_style: description: - Report heading style. - heading4-style: + type: str + heading4_style: description: - Report heading style. - hline-style: + type: str + hline_style: description: - Horizontal line style. - image-style: + type: str + image_style: description: - Image style. + type: str name: description: - Report theme name. required: true - normal-text-style: + type: str + normal_text_style: description: - Normal text style. - numbered-list-style: + type: str + numbered_list_style: description: - Numbered list style. - page-footer-style: + type: str + page_footer_style: description: - Report page footer style. - page-header-style: + type: str + page_header_style: description: - Report page header style. - page-orient: + type: str + page_orient: description: - Report page orientation. + type: str choices: - portrait - landscape - page-style: + page_style: description: - Report page style. - report-subtitle-style: + type: str + report_subtitle_style: description: - Report subtitle style. - report-title-style: + type: str + report_title_style: description: - Report title style. - table-chart-caption-style: + type: str + table_chart_caption_style: description: - Table chart caption style. - table-chart-even-row-style: + type: str + table_chart_even_row_style: description: - Table chart even row style. - table-chart-head-style: + type: str + table_chart_head_style: description: - Table chart head row style. - table-chart-odd-row-style: + type: str + table_chart_odd_row_style: description: - Table chart odd row style. - table-chart-style: + type: str + table_chart_style: description: - Table chart style. - toc-heading1-style: + type: str + toc_heading1_style: description: - Table of contents heading style. - toc-heading2-style: + type: str + toc_heading2_style: description: - Table of contents heading style. - toc-heading3-style: + type: str + toc_heading3_style: description: - Table of contents heading style. - toc-heading4-style: + type: str + toc_heading4_style: description: - Table of contents heading style. - toc-title-style: + type: str + toc_title_style: description: - Table of contents title style. + type: str ''' EXAMPLES = ''' @@ -185,6 +225,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Report themes configuration fortios_report_theme: @@ -193,38 +234,38 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" report_theme: - state: "present" - bullet-list-style: "" - column-count: "1" - default-html-style: "" - default-pdf-style: "" - graph-chart-style: "" - heading1-style: "" - heading2-style: "" - heading3-style: "" - heading4-style: "" - hline-style: "" - image-style: "" + bullet_list_style: "" + column_count: "1" + default_html_style: "" + default_pdf_style: "" + graph_chart_style: "" + heading1_style: "" + heading2_style: "" + heading3_style: "" + heading4_style: "" + hline_style: "" + image_style: "" name: "default_name_14" - normal-text-style: "" - numbered-list-style: "" - page-footer-style: "" - page-header-style: "" - page-orient: "portrait" - page-style: "" - report-subtitle-style: "" - report-title-style: "" - table-chart-caption-style: "" - table-chart-even-row-style: "" - table-chart-head-style: "" - table-chart-odd-row-style: "" - table-chart-style: "" - toc-heading1-style: "" - toc-heading2-style: "" - toc-heading3-style: "" - toc-heading4-style: "" - toc-title-style: "" + normal_text_style: "" + numbered_list_style: "" + page_footer_style: "" + page_header_style: "" + page_orient: "portrait" + page_style: "" + report_subtitle_style: "" + report_title_style: "" + table_chart_caption_style: "" + table_chart_even_row_style: "" + table_chart_head_style: "" + table_chart_odd_row_style: "" + table_chart_style: "" + toc_heading1_style: "" + toc_heading2_style: "" + toc_heading3_style: "" + toc_heading4_style: "" + toc_title_style: "" ''' RETURN = ''' @@ -287,14 +328,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -302,20 +345,20 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_report_theme_data(json): - option_list = ['bullet-list-style', 'column-count', 'default-html-style', - 'default-pdf-style', 'graph-chart-style', 'heading1-style', - 'heading2-style', 'heading3-style', 'heading4-style', - 'hline-style', 'image-style', 'name', - 'normal-text-style', 'numbered-list-style', 'page-footer-style', - 'page-header-style', 'page-orient', 'page-style', - 'report-subtitle-style', 'report-title-style', 'table-chart-caption-style', - 'table-chart-even-row-style', 'table-chart-head-style', 'table-chart-odd-row-style', - 'table-chart-style', 'toc-heading1-style', 'toc-heading2-style', - 'toc-heading3-style', 'toc-heading4-style', 'toc-title-style'] + option_list = ['bullet_list_style', 'column_count', 'default_html_style', + 'default_pdf_style', 'graph_chart_style', 'heading1_style', + 'heading2_style', 'heading3_style', 'heading4_style', + 'hline_style', 'image_style', 'name', + 'normal_text_style', 'numbered_list_style', 'page_footer_style', + 'page_header_style', 'page_orient', 'page_style', + 'report_subtitle_style', 'report_title_style', 'table_chart_caption_style', + 'table_chart_even_row_style', 'table_chart_head_style', 'table_chart_odd_row_style', + 'table_chart_style', 'toc_heading1_style', 'toc_heading2_style', + 'toc_heading3_style', 'toc_heading4_style', 'toc_title_style'] dictionary = {} for attribute in option_list: @@ -325,93 +368,98 @@ def filter_report_theme_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data def report_theme(data, fos): vdom = data['vdom'] + state = data['state'] report_theme_data = data['report_theme'] - flattened_data = flatten_multilists_attributes(report_theme_data) - filtered_data = filter_report_theme_data(flattened_data) - if report_theme_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_report_theme_data(report_theme_data)) + + if state == "present": return fos.set('report', 'theme', data=filtered_data, vdom=vdom) - elif report_theme_data['state'] == "absent": + elif state == "absent": return fos.delete('report', 'theme', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_report(data, fos): - login(data) if data['report_theme']: resp = report_theme(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "report_theme": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, - "bullet-list-style": {"required": False, "type": "str"}, - "column-count": {"required": False, "type": "str", + "bullet_list_style": {"required": False, "type": "str"}, + "column_count": {"required": False, "type": "str", "choices": ["1", "2", "3"]}, - "default-html-style": {"required": False, "type": "str"}, - "default-pdf-style": {"required": False, "type": "str"}, - "graph-chart-style": {"required": False, "type": "str"}, - "heading1-style": {"required": False, "type": "str"}, - "heading2-style": {"required": False, "type": "str"}, - "heading3-style": {"required": False, "type": "str"}, - "heading4-style": {"required": False, "type": "str"}, - "hline-style": {"required": False, "type": "str"}, - "image-style": {"required": False, "type": "str"}, + "default_html_style": {"required": False, "type": "str"}, + "default_pdf_style": {"required": False, "type": "str"}, + "graph_chart_style": {"required": False, "type": "str"}, + "heading1_style": {"required": False, "type": "str"}, + "heading2_style": {"required": False, "type": "str"}, + "heading3_style": {"required": False, "type": "str"}, + "heading4_style": {"required": False, "type": "str"}, + "hline_style": {"required": False, "type": "str"}, + "image_style": {"required": False, "type": "str"}, "name": {"required": True, "type": "str"}, - "normal-text-style": {"required": False, "type": "str"}, - "numbered-list-style": {"required": False, "type": "str"}, - "page-footer-style": {"required": False, "type": "str"}, - "page-header-style": {"required": False, "type": "str"}, - "page-orient": {"required": False, "type": "str", + "normal_text_style": {"required": False, "type": "str"}, + "numbered_list_style": {"required": False, "type": "str"}, + "page_footer_style": {"required": False, "type": "str"}, + "page_header_style": {"required": False, "type": "str"}, + "page_orient": {"required": False, "type": "str", "choices": ["portrait", "landscape"]}, - "page-style": {"required": False, "type": "str"}, - "report-subtitle-style": {"required": False, "type": "str"}, - "report-title-style": {"required": False, "type": "str"}, - "table-chart-caption-style": {"required": False, "type": "str"}, - "table-chart-even-row-style": {"required": False, "type": "str"}, - "table-chart-head-style": {"required": False, "type": "str"}, - "table-chart-odd-row-style": {"required": False, "type": "str"}, - "table-chart-style": {"required": False, "type": "str"}, - "toc-heading1-style": {"required": False, "type": "str"}, - "toc-heading2-style": {"required": False, "type": "str"}, - "toc-heading3-style": {"required": False, "type": "str"}, - "toc-heading4-style": {"required": False, "type": "str"}, - "toc-title-style": {"required": False, "type": "str"} + "page_style": {"required": False, "type": "str"}, + "report_subtitle_style": {"required": False, "type": "str"}, + "report_title_style": {"required": False, "type": "str"}, + "table_chart_caption_style": {"required": False, "type": "str"}, + "table_chart_even_row_style": {"required": False, "type": "str"}, + "table_chart_head_style": {"required": False, "type": "str"}, + "table_chart_odd_row_style": {"required": False, "type": "str"}, + "table_chart_style": {"required": False, "type": "str"}, + "toc_heading1_style": {"required": False, "type": "str"}, + "toc_heading2_style": {"required": False, "type": "str"}, + "toc_heading3_style": {"required": False, "type": "str"}, + "toc_heading4_style": {"required": False, "type": "str"}, + "toc_title_style": {"required": False, "type": "str"} } } @@ -419,15 +467,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_report(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_report(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_report(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_access_list.py b/lib/ansible/modules/network/fortios/fortios_router_access_list.py index a2988e295d5..73ccb90965e 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_access_list.py +++ b/lib/ansible/modules/network/fortios/fortios_router_access_list.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_access_list short_description: Configure access lists in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and access_list category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,76 +41,98 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 router_access_list: description: - Configure access lists. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent comments: description: - Comment. + type: str name: description: - Name. required: true + type: str rule: description: - Rule. + type: list suboptions: action: description: - Permit or deny this IP address and netmask prefix. + type: str choices: - permit - deny - exact-match: + exact_match: description: - Enable/disable exact match. + type: str choices: - enable - disable flags: description: - Flags. + type: int id: description: - Rule ID. required: true + type: int prefix: description: - IPv4 prefix to define regular filter criteria, such as "any" or subnets. + type: str wildcard: description: - Wildcard to define Cisco-style wildcard filter criteria. + type: str ''' EXAMPLES = ''' @@ -123,6 +142,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure access lists. fortios_router_access_list: @@ -131,14 +151,14 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" router_access_list: - state: "present" comments: "" name: "default_name_4" rule: - action: "permit" - exact-match: "enable" + exact_match: "enable" flags: "8" id: "9" prefix: "" @@ -205,14 +225,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -220,7 +242,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_access_list_data(json): @@ -234,68 +256,73 @@ def filter_router_access_list_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data def router_access_list(data, fos): vdom = data['vdom'] + state = data['state'] router_access_list_data = data['router_access_list'] - flattened_data = flatten_multilists_attributes(router_access_list_data) - filtered_data = filter_router_access_list_data(flattened_data) - if router_access_list_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_router_access_list_data(router_access_list_data)) + + if state == "present": return fos.set('router', 'access-list', data=filtered_data, vdom=vdom) - elif router_access_list_data['state'] == "absent": + elif state == "absent": return fos.delete('router', 'access-list', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_access_list']: resp = router_access_list(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "router_access_list": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "comments": {"required": False, "type": "str"}, "name": {"required": True, "type": "str"}, "rule": {"required": False, "type": "list", "options": { "action": {"required": False, "type": "str", "choices": ["permit", "deny"]}, - "exact-match": {"required": False, "type": "str", + "exact_match": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "flags": {"required": False, "type": "int"}, "id": {"required": True, "type": "int"}, @@ -309,15 +336,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_auth_path.py b/lib/ansible/modules/network/fortios/fortios_router_auth_path.py index 5bf082ab198..aeb7f711752 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_auth_path.py +++ b/lib/ansible/modules/network/fortios/fortios_router_auth_path.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_auth_path short_description: Configure authentication based routing in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and auth_path category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,50 +41,66 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 router_auth_path: description: - Configure authentication based routing. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent device: description: - Outgoing interface. Source system.interface.name. + type: str gateway: description: - Gateway IP address. + type: str name: description: - Name of the entry. required: true + type: str ''' EXAMPLES = ''' @@ -97,6 +110,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure authentication based routing. fortios_router_auth_path: @@ -105,8 +119,8 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" router_auth_path: - state: "present" device: " (source system.interface.name)" gateway: "" name: "default_name_5" @@ -172,14 +186,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -187,7 +203,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_auth_path_data(json): @@ -201,61 +217,66 @@ def filter_router_auth_path_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data def router_auth_path(data, fos): vdom = data['vdom'] + state = data['state'] router_auth_path_data = data['router_auth_path'] - flattened_data = flatten_multilists_attributes(router_auth_path_data) - filtered_data = filter_router_auth_path_data(flattened_data) - if router_auth_path_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_router_auth_path_data(router_auth_path_data)) + + if state == "present": return fos.set('router', 'auth-path', data=filtered_data, vdom=vdom) - elif router_auth_path_data['state'] == "absent": + elif state == "absent": return fos.delete('router', 'auth-path', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_auth_path']: resp = router_auth_path(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "router_auth_path": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "device": {"required": False, "type": "str"}, "gateway": {"required": False, "type": "str"}, "name": {"required": True, "type": "str"} @@ -266,15 +287,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_bfd.py b/lib/ansible/modules/network/fortios/fortios_router_bfd.py index faf40c7e176..c1fce847096 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_bfd.py +++ b/lib/ansible/modules/network/fortios/fortios_router_bfd.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_bfd short_description: Configure BFD in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and bfd category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,45 +41,58 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 router_bfd: description: - Configure BFD. default: null + type: dict suboptions: neighbor: description: - neighbor + type: list suboptions: interface: description: - Interface name. Source system.interface.name. + type: str ip: description: - IPv4 address of the BFD neighbor. required: true + type: str ''' EXAMPLES = ''' @@ -92,6 +102,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure BFD. fortios_router_bfd: @@ -167,14 +178,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -182,7 +195,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_bfd_data(json): @@ -196,17 +209,15 @@ def filter_router_bfd_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data @@ -214,33 +225,39 @@ def flatten_multilists_attributes(data): def router_bfd(data, fos): vdom = data['vdom'] router_bfd_data = data['router_bfd'] - flattened_data = flatten_multilists_attributes(router_bfd_data) - filtered_data = filter_router_bfd_data(flattened_data) + filtered_data = underscore_to_hyphen(filter_router_bfd_data(router_bfd_data)) + return fos.set('router', 'bfd', data=filtered_data, vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_bfd']: resp = router_bfd(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, "router_bfd": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { "neighbor": {"required": False, "type": "list", "options": { @@ -254,15 +271,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_bfd6.py b/lib/ansible/modules/network/fortios/fortios_router_bfd6.py index 4f855d465c4..b08803f91b6 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_bfd6.py +++ b/lib/ansible/modules/network/fortios/fortios_router_bfd6.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_bfd6 short_description: Configure IPv6 BFD in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and bfd6 category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,45 +41,57 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 router_bfd6: description: - Configure IPv6 BFD. default: null + type: dict suboptions: neighbor: description: - Configure neighbor of IPv6 BFD. + type: list suboptions: interface: description: - Interface to the BFD neighbor. Source system.interface.name. - ip6-address: + type: str + ip6_address: description: - IPv6 address of the BFD neighbor. - required: true + type: str ''' EXAMPLES = ''' @@ -92,6 +101,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPv6 BFD. fortios_router_bfd6: @@ -104,7 +114,7 @@ EXAMPLES = ''' neighbor: - interface: " (source system.interface.name)" - ip6-address: "" + ip6_address: "" ''' RETURN = ''' @@ -167,14 +177,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -182,7 +194,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_bfd6_data(json): @@ -196,17 +208,15 @@ def filter_router_bfd6_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data @@ -214,38 +224,44 @@ def flatten_multilists_attributes(data): def router_bfd6(data, fos): vdom = data['vdom'] router_bfd6_data = data['router_bfd6'] - flattened_data = flatten_multilists_attributes(router_bfd6_data) - filtered_data = filter_router_bfd6_data(flattened_data) + filtered_data = underscore_to_hyphen(filter_router_bfd6_data(router_bfd6_data)) + return fos.set('router', 'bfd6', data=filtered_data, vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_bfd6']: resp = router_bfd6(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, "router_bfd6": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { "neighbor": {"required": False, "type": "list", "options": { "interface": {"required": False, "type": "str"}, - "ip6-address": {"required": True, "type": "str"} + "ip6_address": {"required": False, "type": "str"} }} } @@ -254,15 +270,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_bgp.py b/lib/ansible/modules/network/fortios/fortios_router_bgp.py index 7e94911136c..9c090de9560 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_bgp.py +++ b/lib/ansible/modules/network/fortios/fortios_router_bgp.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_bgp short_description: Configure BGP in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and bgp category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,58 +41,75 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 router_bgp: description: - Configure BGP. default: null + type: dict suboptions: - admin-distance: + admin_distance: description: - Administrative distance modifications. + type: list suboptions: distance: description: - Administrative distance to apply (1 - 255). + type: int id: description: - ID. required: true - neighbour-prefix: + type: int + neighbour_prefix: description: - Neighbor address prefix. - route-list: + type: str + route_list: description: - Access list of routes to apply new distance to. Source router.access-list.name. - aggregate-address: + type: str + aggregate_address: description: - BGP aggregate address table. + type: list suboptions: - as-set: + as_set: description: - Enable/disable generate AS set path information. + type: str choices: - enable - disable @@ -103,22 +117,27 @@ options: description: - ID. required: true + type: int prefix: description: - Aggregate prefix. - summary-only: + type: str + summary_only: description: - Enable/disable filter more specific routes from updates. + type: str choices: - enable - disable - aggregate-address6: + aggregate_address6: description: - BGP IPv6 aggregate address table. + type: list suboptions: - as-set: + as_set: description: - Enable/disable generate AS set path information. + type: str choices: - enable - disable @@ -126,238 +145,290 @@ options: description: - ID. required: true + type: int prefix6: description: - Aggregate IPv6 prefix. - summary-only: + type: str + summary_only: description: - Enable/disable filter more specific routes from updates. + type: str choices: - enable - disable - always-compare-med: + always_compare_med: description: - Enable/disable always compare MED. + type: str choices: - enable - disable as: description: - Router AS number, valid from 1 to 4294967295, 0 to disable BGP. - bestpath-as-path-ignore: + type: int + bestpath_as_path_ignore: description: - Enable/disable ignore AS path. + type: str choices: - enable - disable - bestpath-cmp-confed-aspath: + bestpath_cmp_confed_aspath: description: - Enable/disable compare federation AS path length. + type: str choices: - enable - disable - bestpath-cmp-routerid: + bestpath_cmp_routerid: description: - Enable/disable compare router ID for identical EBGP paths. + type: str choices: - enable - disable - bestpath-med-confed: + bestpath_med_confed: description: - Enable/disable compare MED among confederation paths. + type: str choices: - enable - disable - bestpath-med-missing-as-worst: + bestpath_med_missing_as_worst: description: - Enable/disable treat missing MED as least preferred. + type: str choices: - enable - disable - client-to-client-reflection: + client_to_client_reflection: description: - Enable/disable client-to-client route reflection. + type: str choices: - enable - disable - cluster-id: + cluster_id: description: - Route reflector cluster ID. - confederation-identifier: + type: str + confederation_identifier: description: - Confederation identifier. - confederation-peers: + type: int + confederation_peers: description: - Confederation peers. + type: list suboptions: peer: description: - Peer ID. required: true + type: str dampening: description: - Enable/disable route-flap dampening. + type: str choices: - enable - disable - dampening-max-suppress-time: + dampening_max_suppress_time: description: - Maximum minutes a route can be suppressed. - dampening-reachability-half-life: + type: int + dampening_reachability_half_life: description: - Reachability half-life time for penalty (min). - dampening-reuse: + type: int + dampening_reuse: description: - Threshold to reuse routes. - dampening-route-map: + type: int + dampening_route_map: description: - Criteria for dampening. Source router.route-map.name. - dampening-suppress: + type: str + dampening_suppress: description: - Threshold to suppress routes. - dampening-unreachability-half-life: + type: int + dampening_unreachability_half_life: description: - Unreachability half-life time for penalty (min). - default-local-preference: + type: int + default_local_preference: description: - Default local preference. - deterministic-med: + type: int + deterministic_med: description: - Enable/disable enforce deterministic comparison of MED. + type: str choices: - enable - disable - distance-external: + distance_external: description: - Distance for routes external to the AS. - distance-internal: + type: int + distance_internal: description: - Distance for routes internal to the AS. - distance-local: + type: int + distance_local: description: - Distance for routes local to the AS. - ebgp-multipath: + type: int + ebgp_multipath: description: - Enable/disable EBGP multi-path. + type: str choices: - enable - disable - enforce-first-as: + enforce_first_as: description: - Enable/disable enforce first AS for EBGP routes. + type: str choices: - enable - disable - fast-external-failover: + fast_external_failover: description: - Enable/disable reset peer BGP session if link goes down. + type: str choices: - enable - disable - graceful-end-on-timer: + graceful_end_on_timer: description: - Enable/disable to exit graceful restart on timer only. + type: str choices: - enable - disable - graceful-restart: + graceful_restart: description: - Enable/disable BGP graceful restart capabilities. + type: str choices: - enable - disable - graceful-restart-time: + graceful_restart_time: description: - Time needed for neighbors to restart (sec). - graceful-stalepath-time: + type: int + graceful_stalepath_time: description: - Time to hold stale paths of restarting neighbor (sec). - graceful-update-delay: + type: int + graceful_update_delay: description: - Route advertisement/selection delay after restart (sec). - holdtime-timer: + type: int + holdtime_timer: description: - Number of seconds to mark peer as dead. - ibgp-multipath: + type: int + ibgp_multipath: description: - Enable/disable IBGP multi-path. + type: str choices: - enable - disable - ignore-optional-capability: + ignore_optional_capability: description: - Don't send unknown optional capability notification message + type: str choices: - enable - disable - keepalive-timer: + keepalive_timer: description: - Frequency to send keep alive requests. - log-neighbour-changes: + type: int + log_neighbour_changes: description: - Enable logging of BGP neighbour's changes + type: str choices: - enable - disable neighbor: description: - BGP neighbor table. + type: list suboptions: activate: description: - Enable/disable address family IPv4 for this neighbor. + type: str choices: - enable - disable activate6: description: - Enable/disable address family IPv6 for this neighbor. + type: str choices: - enable - disable - advertisement-interval: + advertisement_interval: description: - Minimum interval (sec) between sending updates. - allowas-in: + type: int + allowas_in: description: - IPv4 The maximum number of occurrence of my AS number allowed. - allowas-in-enable: + type: int + allowas_in_enable: description: - Enable/disable IPv4 Enable to allow my AS in AS path. + type: str choices: - enable - disable - allowas-in-enable6: + allowas_in_enable6: description: - Enable/disable IPv6 Enable to allow my AS in AS path. + type: str choices: - enable - disable - allowas-in6: + allowas_in6: description: - IPv6 The maximum number of occurrence of my AS number allowed. - as-override: + type: int + as_override: description: - Enable/disable replace peer AS with own AS for IPv4. + type: str choices: - enable - disable - as-override6: + as_override6: description: - Enable/disable replace peer AS with own AS for IPv6. + type: str choices: - enable - disable - attribute-unchanged: + attribute_unchanged: description: - IPv4 List of attributes that should be unchanged. + type: str choices: - as-path - med - next-hop - attribute-unchanged6: + attribute_unchanged6: description: - IPv6 List of attributes that should be unchanged. + type: str choices: - as-path - med @@ -365,294 +436,359 @@ options: bfd: description: - Enable/disable BFD for this neighbor. + type: str choices: - enable - disable - capability-default-originate: + capability_default_originate: description: - Enable/disable advertise default IPv4 route to this neighbor. + type: str choices: - enable - disable - capability-default-originate6: + capability_default_originate6: description: - Enable/disable advertise default IPv6 route to this neighbor. + type: str choices: - enable - disable - capability-dynamic: + capability_dynamic: description: - Enable/disable advertise dynamic capability to this neighbor. + type: str choices: - enable - disable - capability-graceful-restart: + capability_graceful_restart: description: - Enable/disable advertise IPv4 graceful restart capability to this neighbor. + type: str choices: - enable - disable - capability-graceful-restart6: + capability_graceful_restart6: description: - Enable/disable advertise IPv6 graceful restart capability to this neighbor. + type: str choices: - enable - disable - capability-orf: + capability_orf: description: - Accept/Send IPv4 ORF lists to/from this neighbor. + type: str choices: - none - receive - send - both - capability-orf6: + capability_orf6: description: - Accept/Send IPv6 ORF lists to/from this neighbor. + type: str choices: - none - receive - send - both - capability-route-refresh: + capability_route_refresh: description: - Enable/disable advertise route refresh capability to this neighbor. + type: str choices: - enable - disable - conditional-advertise: + conditional_advertise: description: - Conditional advertisement. + type: list suboptions: - advertise-routemap: + advertise_routemap: description: - Name of advertising route map. Source router.route-map.name. - required: true - condition-routemap: + type: str + condition_routemap: description: - Name of condition route map. Source router.route-map.name. - condition-type: + type: str + condition_type: description: - Type of condition. + type: str choices: - exist - non-exist - connect-timer: + connect_timer: description: - Interval (sec) for connect timer. - default-originate-routemap: + type: int + default_originate_routemap: description: - Route map to specify criteria to originate IPv4 default. Source router.route-map.name. - default-originate-routemap6: + type: str + default_originate_routemap6: description: - Route map to specify criteria to originate IPv6 default. Source router.route-map.name. + type: str description: description: - Description. - distribute-list-in: + type: str + distribute_list_in: description: - Filter for IPv4 updates from this neighbor. Source router.access-list.name. - distribute-list-in6: + type: str + distribute_list_in6: description: - Filter for IPv6 updates from this neighbor. Source router.access-list6.name. - distribute-list-out: + type: str + distribute_list_out: description: - Filter for IPv4 updates to this neighbor. Source router.access-list.name. - distribute-list-out6: + type: str + distribute_list_out6: description: - Filter for IPv6 updates to this neighbor. Source router.access-list6.name. - dont-capability-negotiate: + type: str + dont_capability_negotiate: description: - Don't negotiate capabilities with this neighbor + type: str choices: - enable - disable - ebgp-enforce-multihop: + ebgp_enforce_multihop: description: - Enable/disable allow multi-hop EBGP neighbors. + type: str choices: - enable - disable - ebgp-multihop-ttl: + ebgp_multihop_ttl: description: - EBGP multihop TTL for this peer. - filter-list-in: + type: int + filter_list_in: description: - BGP filter for IPv4 inbound routes. Source router.aspath-list.name. - filter-list-in6: + type: str + filter_list_in6: description: - BGP filter for IPv6 inbound routes. Source router.aspath-list.name. - filter-list-out: + type: str + filter_list_out: description: - BGP filter for IPv4 outbound routes. Source router.aspath-list.name. - filter-list-out6: + type: str + filter_list_out6: description: - BGP filter for IPv6 outbound routes. Source router.aspath-list.name. - holdtime-timer: + type: str + holdtime_timer: description: - Interval (sec) before peer considered dead. + type: int interface: description: - Interface Source system.interface.name. + type: str ip: description: - IP/IPv6 address of neighbor. required: true - keep-alive-timer: + type: str + keep_alive_timer: description: - Keep alive timer interval (sec). - link-down-failover: + type: int + link_down_failover: description: - Enable/disable failover upon link down. + type: str choices: - enable - disable - local-as: + local_as: description: - Local AS number of neighbor. - local-as-no-prepend: + type: int + local_as_no_prepend: description: - Do not prepend local-as to incoming updates. + type: str choices: - enable - disable - local-as-replace-as: + local_as_replace_as: description: - Replace real AS with local-as in outgoing updates. + type: str choices: - enable - disable - maximum-prefix: + maximum_prefix: description: - Maximum number of IPv4 prefixes to accept from this peer. - maximum-prefix-threshold: + type: int + maximum_prefix_threshold: description: - Maximum IPv4 prefix threshold value (1 - 100 percent). - maximum-prefix-threshold6: + type: int + maximum_prefix_threshold6: description: - Maximum IPv6 prefix threshold value (1 - 100 percent). - maximum-prefix-warning-only: + type: int + maximum_prefix_warning_only: description: - Enable/disable IPv4 Only give warning message when limit is exceeded. + type: str choices: - enable - disable - maximum-prefix-warning-only6: + maximum_prefix_warning_only6: description: - Enable/disable IPv6 Only give warning message when limit is exceeded. + type: str choices: - enable - disable - maximum-prefix6: + maximum_prefix6: description: - Maximum number of IPv6 prefixes to accept from this peer. - next-hop-self: + type: int + next_hop_self: description: - Enable/disable IPv4 next-hop calculation for this neighbor. + type: str choices: - enable - disable - next-hop-self6: + next_hop_self6: description: - Enable/disable IPv6 next-hop calculation for this neighbor. + type: str choices: - enable - disable - override-capability: + override_capability: description: - Enable/disable override result of capability negotiation. + type: str choices: - enable - disable passive: description: - Enable/disable sending of open messages to this neighbor. + type: str choices: - enable - disable password: description: - Password used in MD5 authentication. - prefix-list-in: + type: str + prefix_list_in: description: - IPv4 Inbound filter for updates from this neighbor. Source router.prefix-list.name. - prefix-list-in6: + type: str + prefix_list_in6: description: - IPv6 Inbound filter for updates from this neighbor. Source router.prefix-list6.name. - prefix-list-out: + type: str + prefix_list_out: description: - IPv4 Outbound filter for updates to this neighbor. Source router.prefix-list.name. - prefix-list-out6: + type: str + prefix_list_out6: description: - IPv6 Outbound filter for updates to this neighbor. Source router.prefix-list6.name. - remote-as: + type: str + remote_as: description: - AS number of neighbor. - remove-private-as: + type: int + remove_private_as: description: - Enable/disable remove private AS number from IPv4 outbound updates. + type: str choices: - enable - disable - remove-private-as6: + remove_private_as6: description: - Enable/disable remove private AS number from IPv6 outbound updates. + type: str choices: - enable - disable - restart-time: + restart_time: description: - Graceful restart delay time (sec, 0 = global default). - retain-stale-time: + type: int + retain_stale_time: description: - Time to retain stale routes. - route-map-in: + type: int + route_map_in: description: - IPv4 Inbound route map filter. Source router.route-map.name. - route-map-in6: + type: str + route_map_in6: description: - IPv6 Inbound route map filter. Source router.route-map.name. - route-map-out: + type: str + route_map_out: description: - IPv4 Outbound route map filter. Source router.route-map.name. - route-map-out6: + type: str + route_map_out6: description: - IPv6 Outbound route map filter. Source router.route-map.name. - route-reflector-client: + type: str + route_reflector_client: description: - Enable/disable IPv4 AS route reflector client. + type: str choices: - enable - disable - route-reflector-client6: + route_reflector_client6: description: - Enable/disable IPv6 AS route reflector client. + type: str choices: - enable - disable - route-server-client: + route_server_client: description: - Enable/disable IPv4 AS route server client. + type: str choices: - enable - disable - route-server-client6: + route_server_client6: description: - Enable/disable IPv6 AS route server client. + type: str choices: - enable - disable - send-community: + send_community: description: - IPv4 Send community attribute to neighbor. + type: str choices: - standard - extended - both - disable - send-community6: + send_community6: description: - IPv6 Send community attribute to neighbor. + type: str choices: - standard - extended @@ -661,104 +797,125 @@ options: shutdown: description: - Enable/disable shutdown this neighbor. + type: str choices: - enable - disable - soft-reconfiguration: + soft_reconfiguration: description: - Enable/disable allow IPv4 inbound soft reconfiguration. + type: str choices: - enable - disable - soft-reconfiguration6: + soft_reconfiguration6: description: - Enable/disable allow IPv6 inbound soft reconfiguration. + type: str choices: - enable - disable - stale-route: + stale_route: description: - Enable/disable stale route after neighbor down. + type: str choices: - enable - disable - strict-capability-match: + strict_capability_match: description: - Enable/disable strict capability matching. + type: str choices: - enable - disable - unsuppress-map: + unsuppress_map: description: - IPv4 Route map to selectively unsuppress suppressed routes. Source router.route-map.name. - unsuppress-map6: + type: str + unsuppress_map6: description: - IPv6 Route map to selectively unsuppress suppressed routes. Source router.route-map.name. - update-source: + type: str + update_source: description: - Interface to use as source IP/IPv6 address of TCP connections. Source system.interface.name. + type: str weight: description: - Neighbor weight. - neighbor-group: + type: int + neighbor_group: description: - BGP neighbor group table. + type: list suboptions: activate: description: - Enable/disable address family IPv4 for this neighbor. + type: str choices: - enable - disable activate6: description: - Enable/disable address family IPv6 for this neighbor. + type: str choices: - enable - disable - advertisement-interval: + advertisement_interval: description: - Minimum interval (sec) between sending updates. - allowas-in: + type: int + allowas_in: description: - IPv4 The maximum number of occurrence of my AS number allowed. - allowas-in-enable: + type: int + allowas_in_enable: description: - Enable/disable IPv4 Enable to allow my AS in AS path. + type: str choices: - enable - disable - allowas-in-enable6: + allowas_in_enable6: description: - Enable/disable IPv6 Enable to allow my AS in AS path. + type: str choices: - enable - disable - allowas-in6: + allowas_in6: description: - IPv6 The maximum number of occurrence of my AS number allowed. - as-override: + type: int + as_override: description: - Enable/disable replace peer AS with own AS for IPv4. + type: str choices: - enable - disable - as-override6: + as_override6: description: - Enable/disable replace peer AS with own AS for IPv6. + type: str choices: - enable - disable - attribute-unchanged: + attribute_unchanged: description: - IPv4 List of attributes that should be unchanged. + type: str choices: - as-path - med - next-hop - attribute-unchanged6: + attribute_unchanged6: description: - IPv6 List of attributes that should be unchanged. + type: str choices: - as-path - med @@ -766,274 +923,335 @@ options: bfd: description: - Enable/disable BFD for this neighbor. + type: str choices: - enable - disable - capability-default-originate: + capability_default_originate: description: - Enable/disable advertise default IPv4 route to this neighbor. + type: str choices: - enable - disable - capability-default-originate6: + capability_default_originate6: description: - Enable/disable advertise default IPv6 route to this neighbor. + type: str choices: - enable - disable - capability-dynamic: + capability_dynamic: description: - Enable/disable advertise dynamic capability to this neighbor. + type: str choices: - enable - disable - capability-graceful-restart: + capability_graceful_restart: description: - Enable/disable advertise IPv4 graceful restart capability to this neighbor. + type: str choices: - enable - disable - capability-graceful-restart6: + capability_graceful_restart6: description: - Enable/disable advertise IPv6 graceful restart capability to this neighbor. + type: str choices: - enable - disable - capability-orf: + capability_orf: description: - Accept/Send IPv4 ORF lists to/from this neighbor. + type: str choices: - none - receive - send - both - capability-orf6: + capability_orf6: description: - Accept/Send IPv6 ORF lists to/from this neighbor. + type: str choices: - none - receive - send - both - capability-route-refresh: + capability_route_refresh: description: - Enable/disable advertise route refresh capability to this neighbor. + type: str choices: - enable - disable - connect-timer: + connect_timer: description: - Interval (sec) for connect timer. - default-originate-routemap: + type: int + default_originate_routemap: description: - Route map to specify criteria to originate IPv4 default. Source router.route-map.name. - default-originate-routemap6: + type: str + default_originate_routemap6: description: - Route map to specify criteria to originate IPv6 default. Source router.route-map.name. + type: str description: description: - Description. - distribute-list-in: + type: str + distribute_list_in: description: - Filter for IPv4 updates from this neighbor. Source router.access-list.name. - distribute-list-in6: + type: str + distribute_list_in6: description: - Filter for IPv6 updates from this neighbor. Source router.access-list6.name. - distribute-list-out: + type: str + distribute_list_out: description: - Filter for IPv4 updates to this neighbor. Source router.access-list.name. - distribute-list-out6: + type: str + distribute_list_out6: description: - Filter for IPv6 updates to this neighbor. Source router.access-list6.name. - dont-capability-negotiate: + type: str + dont_capability_negotiate: description: - Don't negotiate capabilities with this neighbor + type: str choices: - enable - disable - ebgp-enforce-multihop: + ebgp_enforce_multihop: description: - Enable/disable allow multi-hop EBGP neighbors. + type: str choices: - enable - disable - ebgp-multihop-ttl: + ebgp_multihop_ttl: description: - EBGP multihop TTL for this peer. - filter-list-in: + type: int + filter_list_in: description: - BGP filter for IPv4 inbound routes. Source router.aspath-list.name. - filter-list-in6: + type: str + filter_list_in6: description: - BGP filter for IPv6 inbound routes. Source router.aspath-list.name. - filter-list-out: + type: str + filter_list_out: description: - BGP filter for IPv4 outbound routes. Source router.aspath-list.name. - filter-list-out6: + type: str + filter_list_out6: description: - BGP filter for IPv6 outbound routes. Source router.aspath-list.name. - holdtime-timer: + type: str + holdtime_timer: description: - Interval (sec) before peer considered dead. + type: int interface: description: - Interface Source system.interface.name. - keep-alive-timer: + type: str + keep_alive_timer: description: - Keep alive timer interval (sec). - link-down-failover: + type: int + link_down_failover: description: - Enable/disable failover upon link down. + type: str choices: - enable - disable - local-as: + local_as: description: - Local AS number of neighbor. - local-as-no-prepend: + type: int + local_as_no_prepend: description: - Do not prepend local-as to incoming updates. + type: str choices: - enable - disable - local-as-replace-as: + local_as_replace_as: description: - Replace real AS with local-as in outgoing updates. + type: str choices: - enable - disable - maximum-prefix: + maximum_prefix: description: - Maximum number of IPv4 prefixes to accept from this peer. - maximum-prefix-threshold: + type: int + maximum_prefix_threshold: description: - Maximum IPv4 prefix threshold value (1 - 100 percent). - maximum-prefix-threshold6: + type: int + maximum_prefix_threshold6: description: - Maximum IPv6 prefix threshold value (1 - 100 percent). - maximum-prefix-warning-only: + type: int + maximum_prefix_warning_only: description: - Enable/disable IPv4 Only give warning message when limit is exceeded. + type: str choices: - enable - disable - maximum-prefix-warning-only6: + maximum_prefix_warning_only6: description: - Enable/disable IPv6 Only give warning message when limit is exceeded. + type: str choices: - enable - disable - maximum-prefix6: + maximum_prefix6: description: - Maximum number of IPv6 prefixes to accept from this peer. + type: int name: description: - Neighbor group name. required: true - next-hop-self: + type: str + next_hop_self: description: - Enable/disable IPv4 next-hop calculation for this neighbor. + type: str choices: - enable - disable - next-hop-self6: + next_hop_self6: description: - Enable/disable IPv6 next-hop calculation for this neighbor. + type: str choices: - enable - disable - override-capability: + override_capability: description: - Enable/disable override result of capability negotiation. + type: str choices: - enable - disable passive: description: - Enable/disable sending of open messages to this neighbor. + type: str choices: - enable - disable - prefix-list-in: + prefix_list_in: description: - IPv4 Inbound filter for updates from this neighbor. Source router.prefix-list.name. - prefix-list-in6: + type: str + prefix_list_in6: description: - IPv6 Inbound filter for updates from this neighbor. Source router.prefix-list6.name. - prefix-list-out: + type: str + prefix_list_out: description: - IPv4 Outbound filter for updates to this neighbor. Source router.prefix-list.name. - prefix-list-out6: + type: str + prefix_list_out6: description: - IPv6 Outbound filter for updates to this neighbor. Source router.prefix-list6.name. - remote-as: + type: str + remote_as: description: - AS number of neighbor. - remove-private-as: + type: int + remove_private_as: description: - Enable/disable remove private AS number from IPv4 outbound updates. + type: str choices: - enable - disable - remove-private-as6: + remove_private_as6: description: - Enable/disable remove private AS number from IPv6 outbound updates. + type: str choices: - enable - disable - restart-time: + restart_time: description: - Graceful restart delay time (sec, 0 = global default). - retain-stale-time: + type: int + retain_stale_time: description: - Time to retain stale routes. - route-map-in: + type: int + route_map_in: description: - IPv4 Inbound route map filter. Source router.route-map.name. - route-map-in6: + type: str + route_map_in6: description: - IPv6 Inbound route map filter. Source router.route-map.name. - route-map-out: + type: str + route_map_out: description: - IPv4 Outbound route map filter. Source router.route-map.name. - route-map-out6: + type: str + route_map_out6: description: - IPv6 Outbound route map filter. Source router.route-map.name. - route-reflector-client: + type: str + route_reflector_client: description: - Enable/disable IPv4 AS route reflector client. + type: str choices: - enable - disable - route-reflector-client6: + route_reflector_client6: description: - Enable/disable IPv6 AS route reflector client. + type: str choices: - enable - disable - route-server-client: + route_server_client: description: - Enable/disable IPv4 AS route server client. + type: str choices: - enable - disable - route-server-client6: + route_server_client6: description: - Enable/disable IPv6 AS route server client. + type: str choices: - enable - disable - send-community: + send_community: description: - IPv4 Send community attribute to neighbor. + type: str choices: - standard - extended - both - disable - send-community6: + send_community6: description: - IPv6 Send community attribute to neighbor. + type: str choices: - standard - extended @@ -1042,69 +1260,107 @@ options: shutdown: description: - Enable/disable shutdown this neighbor. + type: str choices: - enable - disable - soft-reconfiguration: + soft_reconfiguration: description: - Enable/disable allow IPv4 inbound soft reconfiguration. + type: str choices: - enable - disable - soft-reconfiguration6: + soft_reconfiguration6: description: - Enable/disable allow IPv6 inbound soft reconfiguration. + type: str choices: - enable - disable - stale-route: + stale_route: description: - Enable/disable stale route after neighbor down. + type: str choices: - enable - disable - strict-capability-match: + strict_capability_match: description: - Enable/disable strict capability matching. + type: str choices: - enable - disable - unsuppress-map: + unsuppress_map: description: - IPv4 Route map to selectively unsuppress suppressed routes. Source router.route-map.name. - unsuppress-map6: + type: str + unsuppress_map6: description: - IPv6 Route map to selectively unsuppress suppressed routes. Source router.route-map.name. - update-source: + type: str + update_source: description: - Interface to use as source IP/IPv6 address of TCP connections. Source system.interface.name. + type: str weight: description: - Neighbor weight. - neighbor-range: + type: int + neighbor_range: description: - BGP neighbor range table. + type: list suboptions: id: description: - Neighbor range ID. required: true - max-neighbor-num: + type: int + max_neighbor_num: description: - Maximum number of neighbors. - neighbor-group: + type: int + neighbor_group: description: - Neighbor group name. Source router.bgp.neighbor-group.name. + type: str prefix: description: - Neighbor range prefix. + type: str + neighbor_range6: + description: + - BGP IPv6 neighbor range table. + type: list + suboptions: + id: + description: + - IPv6 neighbor range ID. + required: true + type: int + max_neighbor_num: + description: + - Maximum number of neighbors. + type: int + neighbor_group: + description: + - Neighbor group name. Source router.bgp.neighbor-group.name. + type: str + prefix6: + description: + - IPv6 prefix. + type: str network: description: - BGP network table. + type: list suboptions: backdoor: description: - Enable/disable route as backdoor. + type: str choices: - enable - disable @@ -1112,25 +1368,31 @@ options: description: - ID. required: true + type: int prefix: description: - Network prefix. - route-map: + type: str + route_map: description: - Route map to modify generated route. Source router.route-map.name. - network-import-check: + type: str + network_import_check: description: - Enable/disable ensure BGP network route exists in IGP. + type: str choices: - enable - disable network6: description: - BGP IPv6 network table. + type: list suboptions: backdoor: description: - Enable/disable route as backdoor. + type: str choices: - enable - disable @@ -1138,55 +1400,69 @@ options: description: - ID. required: true + type: int prefix6: description: - Network IPv6 prefix. - route-map: + type: str + route_map: description: - Route map to modify generated route. Source router.route-map.name. + type: str redistribute: description: - BGP IPv4 redistribute table. + type: list suboptions: name: description: - Distribute list entry name. required: true - route-map: + type: str + route_map: description: - Route map name. Source router.route-map.name. + type: str status: description: - Status + type: str choices: - enable - disable redistribute6: description: - BGP IPv6 redistribute table. + type: list suboptions: name: description: - Distribute list entry name. required: true - route-map: + type: str + route_map: description: - Route map name. Source router.route-map.name. + type: str status: description: - Status + type: str choices: - enable - disable - router-id: + router_id: description: - Router ID. - scan-time: + type: str + scan_time: description: - Background scanner interval (sec), 0 to disable it. + type: int synchronization: description: - Enable/disable only advertise routes from iBGP if routes present in an IGP. + type: str choices: - enable - disable @@ -1199,6 +1475,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure BGP. fortios_router_bgp: @@ -1208,265 +1485,271 @@ EXAMPLES = ''' vdom: "{{ vdom }}" https: "False" router_bgp: - admin-distance: + admin_distance: - distance: "4" id: "5" - neighbour-prefix: "" - route-list: " (source router.access-list.name)" - aggregate-address: + neighbour_prefix: "" + route_list: " (source router.access-list.name)" + aggregate_address: - - as-set: "enable" + as_set: "enable" id: "10" prefix: "" - summary-only: "enable" - aggregate-address6: + summary_only: "enable" + aggregate_address6: - - as-set: "enable" + as_set: "enable" id: "15" prefix6: "" - summary-only: "enable" - always-compare-med: "enable" + summary_only: "enable" + always_compare_med: "enable" as: "19" - bestpath-as-path-ignore: "enable" - bestpath-cmp-confed-aspath: "enable" - bestpath-cmp-routerid: "enable" - bestpath-med-confed: "enable" - bestpath-med-missing-as-worst: "enable" - client-to-client-reflection: "enable" - cluster-id: "" - confederation-identifier: "27" - confederation-peers: + bestpath_as_path_ignore: "enable" + bestpath_cmp_confed_aspath: "enable" + bestpath_cmp_routerid: "enable" + bestpath_med_confed: "enable" + bestpath_med_missing_as_worst: "enable" + client_to_client_reflection: "enable" + cluster_id: "" + confederation_identifier: "27" + confederation_peers: - peer: "" dampening: "enable" - dampening-max-suppress-time: "31" - dampening-reachability-half-life: "32" - dampening-reuse: "33" - dampening-route-map: " (source router.route-map.name)" - dampening-suppress: "35" - dampening-unreachability-half-life: "36" - default-local-preference: "37" - deterministic-med: "enable" - distance-external: "39" - distance-internal: "40" - distance-local: "41" - ebgp-multipath: "enable" - enforce-first-as: "enable" - fast-external-failover: "enable" - graceful-end-on-timer: "enable" - graceful-restart: "enable" - graceful-restart-time: "47" - graceful-stalepath-time: "48" - graceful-update-delay: "49" - holdtime-timer: "50" - ibgp-multipath: "enable" - ignore-optional-capability: "enable" - keepalive-timer: "53" - log-neighbour-changes: "enable" + dampening_max_suppress_time: "31" + dampening_reachability_half_life: "32" + dampening_reuse: "33" + dampening_route_map: " (source router.route-map.name)" + dampening_suppress: "35" + dampening_unreachability_half_life: "36" + default_local_preference: "37" + deterministic_med: "enable" + distance_external: "39" + distance_internal: "40" + distance_local: "41" + ebgp_multipath: "enable" + enforce_first_as: "enable" + fast_external_failover: "enable" + graceful_end_on_timer: "enable" + graceful_restart: "enable" + graceful_restart_time: "47" + graceful_stalepath_time: "48" + graceful_update_delay: "49" + holdtime_timer: "50" + ibgp_multipath: "enable" + ignore_optional_capability: "enable" + keepalive_timer: "53" + log_neighbour_changes: "enable" neighbor: - activate: "enable" activate6: "enable" - advertisement-interval: "58" - allowas-in: "59" - allowas-in-enable: "enable" - allowas-in-enable6: "enable" - allowas-in6: "62" - as-override: "enable" - as-override6: "enable" - attribute-unchanged: "as-path" - attribute-unchanged6: "as-path" + advertisement_interval: "58" + allowas_in: "59" + allowas_in_enable: "enable" + allowas_in_enable6: "enable" + allowas_in6: "62" + as_override: "enable" + as_override6: "enable" + attribute_unchanged: "as-path" + attribute_unchanged6: "as-path" bfd: "enable" - capability-default-originate: "enable" - capability-default-originate6: "enable" - capability-dynamic: "enable" - capability-graceful-restart: "enable" - capability-graceful-restart6: "enable" - capability-orf: "none" - capability-orf6: "none" - capability-route-refresh: "enable" - conditional-advertise: + capability_default_originate: "enable" + capability_default_originate6: "enable" + capability_dynamic: "enable" + capability_graceful_restart: "enable" + capability_graceful_restart6: "enable" + capability_orf: "none" + capability_orf6: "none" + capability_route_refresh: "enable" + conditional_advertise: - - advertise-routemap: " (source router.route-map.name)" - condition-routemap: " (source router.route-map.name)" - condition-type: "exist" - connect-timer: "80" - default-originate-routemap: " (source router.route-map.name)" - default-originate-routemap6: " (source router.route-map.name)" + advertise_routemap: " (source router.route-map.name)" + condition_routemap: " (source router.route-map.name)" + condition_type: "exist" + connect_timer: "80" + default_originate_routemap: " (source router.route-map.name)" + default_originate_routemap6: " (source router.route-map.name)" description: "" - distribute-list-in: " (source router.access-list.name)" - distribute-list-in6: " (source router.access-list6.name)" - distribute-list-out: " (source router.access-list.name)" - distribute-list-out6: " (source router.access-list6.name)" - dont-capability-negotiate: "enable" - ebgp-enforce-multihop: "enable" - ebgp-multihop-ttl: "90" - filter-list-in: " (source router.aspath-list.name)" - filter-list-in6: " (source router.aspath-list.name)" - filter-list-out: " (source router.aspath-list.name)" - filter-list-out6: " (source router.aspath-list.name)" - holdtime-timer: "95" + distribute_list_in: " (source router.access-list.name)" + distribute_list_in6: " (source router.access-list6.name)" + distribute_list_out: " (source router.access-list.name)" + distribute_list_out6: " (source router.access-list6.name)" + dont_capability_negotiate: "enable" + ebgp_enforce_multihop: "enable" + ebgp_multihop_ttl: "90" + filter_list_in: " (source router.aspath-list.name)" + filter_list_in6: " (source router.aspath-list.name)" + filter_list_out: " (source router.aspath-list.name)" + filter_list_out6: " (source router.aspath-list.name)" + holdtime_timer: "95" interface: " (source system.interface.name)" ip: "" - keep-alive-timer: "98" - link-down-failover: "enable" - local-as: "100" - local-as-no-prepend: "enable" - local-as-replace-as: "enable" - maximum-prefix: "103" - maximum-prefix-threshold: "104" - maximum-prefix-threshold6: "105" - maximum-prefix-warning-only: "enable" - maximum-prefix-warning-only6: "enable" - maximum-prefix6: "108" - next-hop-self: "enable" - next-hop-self6: "enable" - override-capability: "enable" + keep_alive_timer: "98" + link_down_failover: "enable" + local_as: "100" + local_as_no_prepend: "enable" + local_as_replace_as: "enable" + maximum_prefix: "103" + maximum_prefix_threshold: "104" + maximum_prefix_threshold6: "105" + maximum_prefix_warning_only: "enable" + maximum_prefix_warning_only6: "enable" + maximum_prefix6: "108" + next_hop_self: "enable" + next_hop_self6: "enable" + override_capability: "enable" passive: "enable" password: "" - prefix-list-in: " (source router.prefix-list.name)" - prefix-list-in6: " (source router.prefix-list6.name)" - prefix-list-out: " (source router.prefix-list.name)" - prefix-list-out6: " (source router.prefix-list6.name)" - remote-as: "118" - remove-private-as: "enable" - remove-private-as6: "enable" - restart-time: "121" - retain-stale-time: "122" - route-map-in: " (source router.route-map.name)" - route-map-in6: " (source router.route-map.name)" - route-map-out: " (source router.route-map.name)" - route-map-out6: " (source router.route-map.name)" - route-reflector-client: "enable" - route-reflector-client6: "enable" - route-server-client: "enable" - route-server-client6: "enable" - send-community: "standard" - send-community6: "standard" + prefix_list_in: " (source router.prefix-list.name)" + prefix_list_in6: " (source router.prefix-list6.name)" + prefix_list_out: " (source router.prefix-list.name)" + prefix_list_out6: " (source router.prefix-list6.name)" + remote_as: "118" + remove_private_as: "enable" + remove_private_as6: "enable" + restart_time: "121" + retain_stale_time: "122" + route_map_in: " (source router.route-map.name)" + route_map_in6: " (source router.route-map.name)" + route_map_out: " (source router.route-map.name)" + route_map_out6: " (source router.route-map.name)" + route_reflector_client: "enable" + route_reflector_client6: "enable" + route_server_client: "enable" + route_server_client6: "enable" + send_community: "standard" + send_community6: "standard" shutdown: "enable" - soft-reconfiguration: "enable" - soft-reconfiguration6: "enable" - stale-route: "enable" - strict-capability-match: "enable" - unsuppress-map: " (source router.route-map.name)" - unsuppress-map6: " (source router.route-map.name)" - update-source: " (source system.interface.name)" + soft_reconfiguration: "enable" + soft_reconfiguration6: "enable" + stale_route: "enable" + strict_capability_match: "enable" + unsuppress_map: " (source router.route-map.name)" + unsuppress_map6: " (source router.route-map.name)" + update_source: " (source system.interface.name)" weight: "141" - neighbor-group: + neighbor_group: - activate: "enable" activate6: "enable" - advertisement-interval: "145" - allowas-in: "146" - allowas-in-enable: "enable" - allowas-in-enable6: "enable" - allowas-in6: "149" - as-override: "enable" - as-override6: "enable" - attribute-unchanged: "as-path" - attribute-unchanged6: "as-path" + advertisement_interval: "145" + allowas_in: "146" + allowas_in_enable: "enable" + allowas_in_enable6: "enable" + allowas_in6: "149" + as_override: "enable" + as_override6: "enable" + attribute_unchanged: "as-path" + attribute_unchanged6: "as-path" bfd: "enable" - capability-default-originate: "enable" - capability-default-originate6: "enable" - capability-dynamic: "enable" - capability-graceful-restart: "enable" - capability-graceful-restart6: "enable" - capability-orf: "none" - capability-orf6: "none" - capability-route-refresh: "enable" - connect-timer: "163" - default-originate-routemap: " (source router.route-map.name)" - default-originate-routemap6: " (source router.route-map.name)" + capability_default_originate: "enable" + capability_default_originate6: "enable" + capability_dynamic: "enable" + capability_graceful_restart: "enable" + capability_graceful_restart6: "enable" + capability_orf: "none" + capability_orf6: "none" + capability_route_refresh: "enable" + connect_timer: "163" + default_originate_routemap: " (source router.route-map.name)" + default_originate_routemap6: " (source router.route-map.name)" description: "" - distribute-list-in: " (source router.access-list.name)" - distribute-list-in6: " (source router.access-list6.name)" - distribute-list-out: " (source router.access-list.name)" - distribute-list-out6: " (source router.access-list6.name)" - dont-capability-negotiate: "enable" - ebgp-enforce-multihop: "enable" - ebgp-multihop-ttl: "173" - filter-list-in: " (source router.aspath-list.name)" - filter-list-in6: " (source router.aspath-list.name)" - filter-list-out: " (source router.aspath-list.name)" - filter-list-out6: " (source router.aspath-list.name)" - holdtime-timer: "178" + distribute_list_in: " (source router.access-list.name)" + distribute_list_in6: " (source router.access-list6.name)" + distribute_list_out: " (source router.access-list.name)" + distribute_list_out6: " (source router.access-list6.name)" + dont_capability_negotiate: "enable" + ebgp_enforce_multihop: "enable" + ebgp_multihop_ttl: "173" + filter_list_in: " (source router.aspath-list.name)" + filter_list_in6: " (source router.aspath-list.name)" + filter_list_out: " (source router.aspath-list.name)" + filter_list_out6: " (source router.aspath-list.name)" + holdtime_timer: "178" interface: " (source system.interface.name)" - keep-alive-timer: "180" - link-down-failover: "enable" - local-as: "182" - local-as-no-prepend: "enable" - local-as-replace-as: "enable" - maximum-prefix: "185" - maximum-prefix-threshold: "186" - maximum-prefix-threshold6: "187" - maximum-prefix-warning-only: "enable" - maximum-prefix-warning-only6: "enable" - maximum-prefix6: "190" + keep_alive_timer: "180" + link_down_failover: "enable" + local_as: "182" + local_as_no_prepend: "enable" + local_as_replace_as: "enable" + maximum_prefix: "185" + maximum_prefix_threshold: "186" + maximum_prefix_threshold6: "187" + maximum_prefix_warning_only: "enable" + maximum_prefix_warning_only6: "enable" + maximum_prefix6: "190" name: "default_name_191" - next-hop-self: "enable" - next-hop-self6: "enable" - override-capability: "enable" + next_hop_self: "enable" + next_hop_self6: "enable" + override_capability: "enable" passive: "enable" - prefix-list-in: " (source router.prefix-list.name)" - prefix-list-in6: " (source router.prefix-list6.name)" - prefix-list-out: " (source router.prefix-list.name)" - prefix-list-out6: " (source router.prefix-list6.name)" - remote-as: "200" - remove-private-as: "enable" - remove-private-as6: "enable" - restart-time: "203" - retain-stale-time: "204" - route-map-in: " (source router.route-map.name)" - route-map-in6: " (source router.route-map.name)" - route-map-out: " (source router.route-map.name)" - route-map-out6: " (source router.route-map.name)" - route-reflector-client: "enable" - route-reflector-client6: "enable" - route-server-client: "enable" - route-server-client6: "enable" - send-community: "standard" - send-community6: "standard" + prefix_list_in: " (source router.prefix-list.name)" + prefix_list_in6: " (source router.prefix-list6.name)" + prefix_list_out: " (source router.prefix-list.name)" + prefix_list_out6: " (source router.prefix-list6.name)" + remote_as: "200" + remove_private_as: "enable" + remove_private_as6: "enable" + restart_time: "203" + retain_stale_time: "204" + route_map_in: " (source router.route-map.name)" + route_map_in6: " (source router.route-map.name)" + route_map_out: " (source router.route-map.name)" + route_map_out6: " (source router.route-map.name)" + route_reflector_client: "enable" + route_reflector_client6: "enable" + route_server_client: "enable" + route_server_client6: "enable" + send_community: "standard" + send_community6: "standard" shutdown: "enable" - soft-reconfiguration: "enable" - soft-reconfiguration6: "enable" - stale-route: "enable" - strict-capability-match: "enable" - unsuppress-map: " (source router.route-map.name)" - unsuppress-map6: " (source router.route-map.name)" - update-source: " (source system.interface.name)" + soft_reconfiguration: "enable" + soft_reconfiguration6: "enable" + stale_route: "enable" + strict_capability_match: "enable" + unsuppress_map: " (source router.route-map.name)" + unsuppress_map6: " (source router.route-map.name)" + update_source: " (source system.interface.name)" weight: "223" - neighbor-range: + neighbor_range: - id: "225" - max-neighbor-num: "226" - neighbor-group: " (source router.bgp.neighbor-group.name)" + max_neighbor_num: "226" + neighbor_group: " (source router.bgp.neighbor-group.name)" prefix: "" + neighbor_range6: + - + id: "230" + max_neighbor_num: "231" + neighbor_group: " (source router.bgp.neighbor-group.name)" + prefix6: "" network: - backdoor: "enable" - id: "231" + id: "236" prefix: "" - route-map: " (source router.route-map.name)" - network-import-check: "enable" + route_map: " (source router.route-map.name)" + network_import_check: "enable" network6: - backdoor: "enable" - id: "237" + id: "242" prefix6: "" - route-map: " (source router.route-map.name)" + route_map: " (source router.route-map.name)" redistribute: - - name: "default_name_241" - route-map: " (source router.route-map.name)" + name: "default_name_246" + route_map: " (source router.route-map.name)" status: "enable" redistribute6: - - name: "default_name_245" - route-map: " (source router.route-map.name)" + name: "default_name_250" + route_map: " (source router.route-map.name)" status: "enable" - router-id: "" - scan-time: "249" + router_id: "" + scan_time: "254" synchronization: "enable" ''' @@ -1530,14 +1813,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -1545,27 +1830,27 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_bgp_data(json): - option_list = ['admin-distance', 'aggregate-address', 'aggregate-address6', - 'always-compare-med', 'as', 'bestpath-as-path-ignore', - 'bestpath-cmp-confed-aspath', 'bestpath-cmp-routerid', 'bestpath-med-confed', - 'bestpath-med-missing-as-worst', 'client-to-client-reflection', 'cluster-id', - 'confederation-identifier', 'confederation-peers', 'dampening', - 'dampening-max-suppress-time', 'dampening-reachability-half-life', 'dampening-reuse', - 'dampening-route-map', 'dampening-suppress', 'dampening-unreachability-half-life', - 'default-local-preference', 'deterministic-med', 'distance-external', - 'distance-internal', 'distance-local', 'ebgp-multipath', - 'enforce-first-as', 'fast-external-failover', 'graceful-end-on-timer', - 'graceful-restart', 'graceful-restart-time', 'graceful-stalepath-time', - 'graceful-update-delay', 'holdtime-timer', 'ibgp-multipath', - 'ignore-optional-capability', 'keepalive-timer', 'log-neighbour-changes', - 'neighbor', 'neighbor-group', 'neighbor-range', - 'network', 'network-import-check', 'network6', - 'redistribute', 'redistribute6', 'router-id', - 'scan-time', 'synchronization'] + option_list = ['admin_distance', 'aggregate_address', 'aggregate_address6', + 'always_compare_med', 'as', 'bestpath_as_path_ignore', + 'bestpath_cmp_confed_aspath', 'bestpath_cmp_routerid', 'bestpath_med_confed', + 'bestpath_med_missing_as_worst', 'client_to_client_reflection', 'cluster_id', + 'confederation_identifier', 'confederation_peers', 'dampening', + 'dampening_max_suppress_time', 'dampening_reachability_half_life', 'dampening_reuse', + 'dampening_route_map', 'dampening_suppress', 'dampening_unreachability_half_life', + 'default_local_preference', 'deterministic_med', 'distance_external', + 'distance_internal', 'distance_local', 'ebgp_multipath', + 'enforce_first_as', 'fast_external_failover', 'graceful_end_on_timer', + 'graceful_restart', 'graceful_restart_time', 'graceful_stalepath_time', + 'graceful_update_delay', 'holdtime_timer', 'ibgp_multipath', + 'ignore_optional_capability', 'keepalive_timer', 'log_neighbour_changes', + 'neighbor', 'neighbor_group', 'neighbor_range', + 'neighbor_range6', 'network', 'network_import_check', + 'network6', 'redistribute', 'redistribute6', + 'router_id', 'scan_time', 'synchronization'] dictionary = {} for attribute in option_list: @@ -1575,17 +1860,15 @@ def filter_router_bgp_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data @@ -1593,114 +1876,120 @@ def flatten_multilists_attributes(data): def router_bgp(data, fos): vdom = data['vdom'] router_bgp_data = data['router_bgp'] - flattened_data = flatten_multilists_attributes(router_bgp_data) - filtered_data = filter_router_bgp_data(flattened_data) + filtered_data = underscore_to_hyphen(filter_router_bgp_data(router_bgp_data)) + return fos.set('router', 'bgp', data=filtered_data, vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_bgp']: resp = router_bgp(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, "router_bgp": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "admin-distance": {"required": False, "type": "list", + "admin_distance": {"required": False, "type": "list", "options": { "distance": {"required": False, "type": "int"}, "id": {"required": True, "type": "int"}, - "neighbour-prefix": {"required": False, "type": "str"}, - "route-list": {"required": False, "type": "str"} + "neighbour_prefix": {"required": False, "type": "str"}, + "route_list": {"required": False, "type": "str"} }}, - "aggregate-address": {"required": False, "type": "list", + "aggregate_address": {"required": False, "type": "list", "options": { - "as-set": {"required": False, "type": "str", + "as_set": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "id": {"required": True, "type": "int"}, "prefix": {"required": False, "type": "str"}, - "summary-only": {"required": False, "type": "str", + "summary_only": {"required": False, "type": "str", "choices": ["enable", "disable"]} }}, - "aggregate-address6": {"required": False, "type": "list", + "aggregate_address6": {"required": False, "type": "list", "options": { - "as-set": {"required": False, "type": "str", + "as_set": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "id": {"required": True, "type": "int"}, "prefix6": {"required": False, "type": "str"}, - "summary-only": {"required": False, "type": "str", + "summary_only": {"required": False, "type": "str", "choices": ["enable", "disable"]} }}, - "always-compare-med": {"required": False, "type": "str", + "always_compare_med": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "as": {"required": False, "type": "int"}, - "bestpath-as-path-ignore": {"required": False, "type": "str", + "bestpath_as_path_ignore": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "bestpath-cmp-confed-aspath": {"required": False, "type": "str", + "bestpath_cmp_confed_aspath": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "bestpath-cmp-routerid": {"required": False, "type": "str", + "bestpath_cmp_routerid": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "bestpath-med-confed": {"required": False, "type": "str", + "bestpath_med_confed": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "bestpath-med-missing-as-worst": {"required": False, "type": "str", + "bestpath_med_missing_as_worst": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "client-to-client-reflection": {"required": False, "type": "str", + "client_to_client_reflection": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "cluster-id": {"required": False, "type": "str"}, - "confederation-identifier": {"required": False, "type": "int"}, - "confederation-peers": {"required": False, "type": "list", + "cluster_id": {"required": False, "type": "str"}, + "confederation_identifier": {"required": False, "type": "int"}, + "confederation_peers": {"required": False, "type": "list", "options": { "peer": {"required": True, "type": "str"} }}, "dampening": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "dampening-max-suppress-time": {"required": False, "type": "int"}, - "dampening-reachability-half-life": {"required": False, "type": "int"}, - "dampening-reuse": {"required": False, "type": "int"}, - "dampening-route-map": {"required": False, "type": "str"}, - "dampening-suppress": {"required": False, "type": "int"}, - "dampening-unreachability-half-life": {"required": False, "type": "int"}, - "default-local-preference": {"required": False, "type": "int"}, - "deterministic-med": {"required": False, "type": "str", + "dampening_max_suppress_time": {"required": False, "type": "int"}, + "dampening_reachability_half_life": {"required": False, "type": "int"}, + "dampening_reuse": {"required": False, "type": "int"}, + "dampening_route_map": {"required": False, "type": "str"}, + "dampening_suppress": {"required": False, "type": "int"}, + "dampening_unreachability_half_life": {"required": False, "type": "int"}, + "default_local_preference": {"required": False, "type": "int"}, + "deterministic_med": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "distance-external": {"required": False, "type": "int"}, - "distance-internal": {"required": False, "type": "int"}, - "distance-local": {"required": False, "type": "int"}, - "ebgp-multipath": {"required": False, "type": "str", + "distance_external": {"required": False, "type": "int"}, + "distance_internal": {"required": False, "type": "int"}, + "distance_local": {"required": False, "type": "int"}, + "ebgp_multipath": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "enforce-first-as": {"required": False, "type": "str", + "enforce_first_as": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "fast-external-failover": {"required": False, "type": "str", + "fast_external_failover": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "graceful-end-on-timer": {"required": False, "type": "str", + "graceful_end_on_timer": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "graceful-restart": {"required": False, "type": "str", + "graceful_restart": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "graceful-restart-time": {"required": False, "type": "int"}, - "graceful-stalepath-time": {"required": False, "type": "int"}, - "graceful-update-delay": {"required": False, "type": "int"}, - "holdtime-timer": {"required": False, "type": "int"}, - "ibgp-multipath": {"required": False, "type": "str", + "graceful_restart_time": {"required": False, "type": "int"}, + "graceful_stalepath_time": {"required": False, "type": "int"}, + "graceful_update_delay": {"required": False, "type": "int"}, + "holdtime_timer": {"required": False, "type": "int"}, + "ibgp_multipath": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ignore-optional-capability": {"required": False, "type": "str", + "ignore_optional_capability": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "keepalive-timer": {"required": False, "type": "int"}, - "log-neighbour-changes": {"required": False, "type": "str", + "keepalive_timer": {"required": False, "type": "int"}, + "log_neighbour_changes": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "neighbor": {"required": False, "type": "list", "options": { @@ -1708,282 +1997,289 @@ def main(): "choices": ["enable", "disable"]}, "activate6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "advertisement-interval": {"required": False, "type": "int"}, - "allowas-in": {"required": False, "type": "int"}, - "allowas-in-enable": {"required": False, "type": "str", + "advertisement_interval": {"required": False, "type": "int"}, + "allowas_in": {"required": False, "type": "int"}, + "allowas_in_enable": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "allowas-in-enable6": {"required": False, "type": "str", + "allowas_in_enable6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "allowas-in6": {"required": False, "type": "int"}, - "as-override": {"required": False, "type": "str", + "allowas_in6": {"required": False, "type": "int"}, + "as_override": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "as-override6": {"required": False, "type": "str", + "as_override6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "attribute-unchanged": {"required": False, "type": "str", + "attribute_unchanged": {"required": False, "type": "str", "choices": ["as-path", "med", "next-hop"]}, - "attribute-unchanged6": {"required": False, "type": "str", + "attribute_unchanged6": {"required": False, "type": "str", "choices": ["as-path", "med", "next-hop"]}, "bfd": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "capability-default-originate": {"required": False, "type": "str", + "capability_default_originate": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "capability-default-originate6": {"required": False, "type": "str", + "capability_default_originate6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "capability-dynamic": {"required": False, "type": "str", + "capability_dynamic": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "capability-graceful-restart": {"required": False, "type": "str", + "capability_graceful_restart": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "capability-graceful-restart6": {"required": False, "type": "str", + "capability_graceful_restart6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "capability-orf": {"required": False, "type": "str", + "capability_orf": {"required": False, "type": "str", "choices": ["none", "receive", "send", "both"]}, - "capability-orf6": {"required": False, "type": "str", + "capability_orf6": {"required": False, "type": "str", "choices": ["none", "receive", "send", "both"]}, - "capability-route-refresh": {"required": False, "type": "str", + "capability_route_refresh": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "conditional-advertise": {"required": False, "type": "list", + "conditional_advertise": {"required": False, "type": "list", "options": { - "advertise-routemap": {"required": True, "type": "str"}, - "condition-routemap": {"required": False, "type": "str"}, - "condition-type": {"required": False, "type": "str", + "advertise_routemap": {"required": False, "type": "str"}, + "condition_routemap": {"required": False, "type": "str"}, + "condition_type": {"required": False, "type": "str", "choices": ["exist", "non-exist"]} }}, - "connect-timer": {"required": False, "type": "int"}, - "default-originate-routemap": {"required": False, "type": "str"}, - "default-originate-routemap6": {"required": False, "type": "str"}, + "connect_timer": {"required": False, "type": "int"}, + "default_originate_routemap": {"required": False, "type": "str"}, + "default_originate_routemap6": {"required": False, "type": "str"}, "description": {"required": False, "type": "str"}, - "distribute-list-in": {"required": False, "type": "str"}, - "distribute-list-in6": {"required": False, "type": "str"}, - "distribute-list-out": {"required": False, "type": "str"}, - "distribute-list-out6": {"required": False, "type": "str"}, - "dont-capability-negotiate": {"required": False, "type": "str", + "distribute_list_in": {"required": False, "type": "str"}, + "distribute_list_in6": {"required": False, "type": "str"}, + "distribute_list_out": {"required": False, "type": "str"}, + "distribute_list_out6": {"required": False, "type": "str"}, + "dont_capability_negotiate": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ebgp-enforce-multihop": {"required": False, "type": "str", + "ebgp_enforce_multihop": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ebgp-multihop-ttl": {"required": False, "type": "int"}, - "filter-list-in": {"required": False, "type": "str"}, - "filter-list-in6": {"required": False, "type": "str"}, - "filter-list-out": {"required": False, "type": "str"}, - "filter-list-out6": {"required": False, "type": "str"}, - "holdtime-timer": {"required": False, "type": "int"}, + "ebgp_multihop_ttl": {"required": False, "type": "int"}, + "filter_list_in": {"required": False, "type": "str"}, + "filter_list_in6": {"required": False, "type": "str"}, + "filter_list_out": {"required": False, "type": "str"}, + "filter_list_out6": {"required": False, "type": "str"}, + "holdtime_timer": {"required": False, "type": "int"}, "interface": {"required": False, "type": "str"}, "ip": {"required": True, "type": "str"}, - "keep-alive-timer": {"required": False, "type": "int"}, - "link-down-failover": {"required": False, "type": "str", + "keep_alive_timer": {"required": False, "type": "int"}, + "link_down_failover": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "local-as": {"required": False, "type": "int"}, - "local-as-no-prepend": {"required": False, "type": "str", + "local_as": {"required": False, "type": "int"}, + "local_as_no_prepend": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "local-as-replace-as": {"required": False, "type": "str", + "local_as_replace_as": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "maximum-prefix": {"required": False, "type": "int"}, - "maximum-prefix-threshold": {"required": False, "type": "int"}, - "maximum-prefix-threshold6": {"required": False, "type": "int"}, - "maximum-prefix-warning-only": {"required": False, "type": "str", + "maximum_prefix": {"required": False, "type": "int"}, + "maximum_prefix_threshold": {"required": False, "type": "int"}, + "maximum_prefix_threshold6": {"required": False, "type": "int"}, + "maximum_prefix_warning_only": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "maximum-prefix-warning-only6": {"required": False, "type": "str", + "maximum_prefix_warning_only6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "maximum-prefix6": {"required": False, "type": "int"}, - "next-hop-self": {"required": False, "type": "str", + "maximum_prefix6": {"required": False, "type": "int"}, + "next_hop_self": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "next-hop-self6": {"required": False, "type": "str", + "next_hop_self6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-capability": {"required": False, "type": "str", + "override_capability": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "passive": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "password": {"required": False, "type": "str"}, - "prefix-list-in": {"required": False, "type": "str"}, - "prefix-list-in6": {"required": False, "type": "str"}, - "prefix-list-out": {"required": False, "type": "str"}, - "prefix-list-out6": {"required": False, "type": "str"}, - "remote-as": {"required": False, "type": "int"}, - "remove-private-as": {"required": False, "type": "str", + "prefix_list_in": {"required": False, "type": "str"}, + "prefix_list_in6": {"required": False, "type": "str"}, + "prefix_list_out": {"required": False, "type": "str"}, + "prefix_list_out6": {"required": False, "type": "str"}, + "remote_as": {"required": False, "type": "int"}, + "remove_private_as": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "remove-private-as6": {"required": False, "type": "str", + "remove_private_as6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "restart-time": {"required": False, "type": "int"}, - "retain-stale-time": {"required": False, "type": "int"}, - "route-map-in": {"required": False, "type": "str"}, - "route-map-in6": {"required": False, "type": "str"}, - "route-map-out": {"required": False, "type": "str"}, - "route-map-out6": {"required": False, "type": "str"}, - "route-reflector-client": {"required": False, "type": "str", + "restart_time": {"required": False, "type": "int"}, + "retain_stale_time": {"required": False, "type": "int"}, + "route_map_in": {"required": False, "type": "str"}, + "route_map_in6": {"required": False, "type": "str"}, + "route_map_out": {"required": False, "type": "str"}, + "route_map_out6": {"required": False, "type": "str"}, + "route_reflector_client": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "route-reflector-client6": {"required": False, "type": "str", + "route_reflector_client6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "route-server-client": {"required": False, "type": "str", + "route_server_client": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "route-server-client6": {"required": False, "type": "str", + "route_server_client6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "send-community": {"required": False, "type": "str", + "send_community": {"required": False, "type": "str", "choices": ["standard", "extended", "both", "disable"]}, - "send-community6": {"required": False, "type": "str", + "send_community6": {"required": False, "type": "str", "choices": ["standard", "extended", "both", "disable"]}, "shutdown": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "soft-reconfiguration": {"required": False, "type": "str", + "soft_reconfiguration": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "soft-reconfiguration6": {"required": False, "type": "str", + "soft_reconfiguration6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "stale-route": {"required": False, "type": "str", + "stale_route": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "strict-capability-match": {"required": False, "type": "str", + "strict_capability_match": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "unsuppress-map": {"required": False, "type": "str"}, - "unsuppress-map6": {"required": False, "type": "str"}, - "update-source": {"required": False, "type": "str"}, + "unsuppress_map": {"required": False, "type": "str"}, + "unsuppress_map6": {"required": False, "type": "str"}, + "update_source": {"required": False, "type": "str"}, "weight": {"required": False, "type": "int"} }}, - "neighbor-group": {"required": False, "type": "list", + "neighbor_group": {"required": False, "type": "list", "options": { "activate": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "activate6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "advertisement-interval": {"required": False, "type": "int"}, - "allowas-in": {"required": False, "type": "int"}, - "allowas-in-enable": {"required": False, "type": "str", + "advertisement_interval": {"required": False, "type": "int"}, + "allowas_in": {"required": False, "type": "int"}, + "allowas_in_enable": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "allowas-in-enable6": {"required": False, "type": "str", + "allowas_in_enable6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "allowas-in6": {"required": False, "type": "int"}, - "as-override": {"required": False, "type": "str", + "allowas_in6": {"required": False, "type": "int"}, + "as_override": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "as-override6": {"required": False, "type": "str", + "as_override6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "attribute-unchanged": {"required": False, "type": "str", + "attribute_unchanged": {"required": False, "type": "str", "choices": ["as-path", "med", "next-hop"]}, - "attribute-unchanged6": {"required": False, "type": "str", + "attribute_unchanged6": {"required": False, "type": "str", "choices": ["as-path", "med", "next-hop"]}, "bfd": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "capability-default-originate": {"required": False, "type": "str", + "capability_default_originate": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "capability-default-originate6": {"required": False, "type": "str", + "capability_default_originate6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "capability-dynamic": {"required": False, "type": "str", + "capability_dynamic": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "capability-graceful-restart": {"required": False, "type": "str", + "capability_graceful_restart": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "capability-graceful-restart6": {"required": False, "type": "str", + "capability_graceful_restart6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "capability-orf": {"required": False, "type": "str", + "capability_orf": {"required": False, "type": "str", "choices": ["none", "receive", "send", "both"]}, - "capability-orf6": {"required": False, "type": "str", + "capability_orf6": {"required": False, "type": "str", "choices": ["none", "receive", "send", "both"]}, - "capability-route-refresh": {"required": False, "type": "str", + "capability_route_refresh": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "connect-timer": {"required": False, "type": "int"}, - "default-originate-routemap": {"required": False, "type": "str"}, - "default-originate-routemap6": {"required": False, "type": "str"}, + "connect_timer": {"required": False, "type": "int"}, + "default_originate_routemap": {"required": False, "type": "str"}, + "default_originate_routemap6": {"required": False, "type": "str"}, "description": {"required": False, "type": "str"}, - "distribute-list-in": {"required": False, "type": "str"}, - "distribute-list-in6": {"required": False, "type": "str"}, - "distribute-list-out": {"required": False, "type": "str"}, - "distribute-list-out6": {"required": False, "type": "str"}, - "dont-capability-negotiate": {"required": False, "type": "str", + "distribute_list_in": {"required": False, "type": "str"}, + "distribute_list_in6": {"required": False, "type": "str"}, + "distribute_list_out": {"required": False, "type": "str"}, + "distribute_list_out6": {"required": False, "type": "str"}, + "dont_capability_negotiate": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ebgp-enforce-multihop": {"required": False, "type": "str", + "ebgp_enforce_multihop": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ebgp-multihop-ttl": {"required": False, "type": "int"}, - "filter-list-in": {"required": False, "type": "str"}, - "filter-list-in6": {"required": False, "type": "str"}, - "filter-list-out": {"required": False, "type": "str"}, - "filter-list-out6": {"required": False, "type": "str"}, - "holdtime-timer": {"required": False, "type": "int"}, + "ebgp_multihop_ttl": {"required": False, "type": "int"}, + "filter_list_in": {"required": False, "type": "str"}, + "filter_list_in6": {"required": False, "type": "str"}, + "filter_list_out": {"required": False, "type": "str"}, + "filter_list_out6": {"required": False, "type": "str"}, + "holdtime_timer": {"required": False, "type": "int"}, "interface": {"required": False, "type": "str"}, - "keep-alive-timer": {"required": False, "type": "int"}, - "link-down-failover": {"required": False, "type": "str", + "keep_alive_timer": {"required": False, "type": "int"}, + "link_down_failover": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "local-as": {"required": False, "type": "int"}, - "local-as-no-prepend": {"required": False, "type": "str", + "local_as": {"required": False, "type": "int"}, + "local_as_no_prepend": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "local-as-replace-as": {"required": False, "type": "str", + "local_as_replace_as": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "maximum-prefix": {"required": False, "type": "int"}, - "maximum-prefix-threshold": {"required": False, "type": "int"}, - "maximum-prefix-threshold6": {"required": False, "type": "int"}, - "maximum-prefix-warning-only": {"required": False, "type": "str", + "maximum_prefix": {"required": False, "type": "int"}, + "maximum_prefix_threshold": {"required": False, "type": "int"}, + "maximum_prefix_threshold6": {"required": False, "type": "int"}, + "maximum_prefix_warning_only": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "maximum-prefix-warning-only6": {"required": False, "type": "str", + "maximum_prefix_warning_only6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "maximum-prefix6": {"required": False, "type": "int"}, + "maximum_prefix6": {"required": False, "type": "int"}, "name": {"required": True, "type": "str"}, - "next-hop-self": {"required": False, "type": "str", + "next_hop_self": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "next-hop-self6": {"required": False, "type": "str", + "next_hop_self6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "override-capability": {"required": False, "type": "str", + "override_capability": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "passive": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "prefix-list-in": {"required": False, "type": "str"}, - "prefix-list-in6": {"required": False, "type": "str"}, - "prefix-list-out": {"required": False, "type": "str"}, - "prefix-list-out6": {"required": False, "type": "str"}, - "remote-as": {"required": False, "type": "int"}, - "remove-private-as": {"required": False, "type": "str", + "prefix_list_in": {"required": False, "type": "str"}, + "prefix_list_in6": {"required": False, "type": "str"}, + "prefix_list_out": {"required": False, "type": "str"}, + "prefix_list_out6": {"required": False, "type": "str"}, + "remote_as": {"required": False, "type": "int"}, + "remove_private_as": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "remove-private-as6": {"required": False, "type": "str", + "remove_private_as6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "restart-time": {"required": False, "type": "int"}, - "retain-stale-time": {"required": False, "type": "int"}, - "route-map-in": {"required": False, "type": "str"}, - "route-map-in6": {"required": False, "type": "str"}, - "route-map-out": {"required": False, "type": "str"}, - "route-map-out6": {"required": False, "type": "str"}, - "route-reflector-client": {"required": False, "type": "str", + "restart_time": {"required": False, "type": "int"}, + "retain_stale_time": {"required": False, "type": "int"}, + "route_map_in": {"required": False, "type": "str"}, + "route_map_in6": {"required": False, "type": "str"}, + "route_map_out": {"required": False, "type": "str"}, + "route_map_out6": {"required": False, "type": "str"}, + "route_reflector_client": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "route-reflector-client6": {"required": False, "type": "str", + "route_reflector_client6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "route-server-client": {"required": False, "type": "str", + "route_server_client": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "route-server-client6": {"required": False, "type": "str", + "route_server_client6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "send-community": {"required": False, "type": "str", + "send_community": {"required": False, "type": "str", "choices": ["standard", "extended", "both", "disable"]}, - "send-community6": {"required": False, "type": "str", + "send_community6": {"required": False, "type": "str", "choices": ["standard", "extended", "both", "disable"]}, "shutdown": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "soft-reconfiguration": {"required": False, "type": "str", + "soft_reconfiguration": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "soft-reconfiguration6": {"required": False, "type": "str", + "soft_reconfiguration6": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "stale-route": {"required": False, "type": "str", + "stale_route": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "strict-capability-match": {"required": False, "type": "str", + "strict_capability_match": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "unsuppress-map": {"required": False, "type": "str"}, - "unsuppress-map6": {"required": False, "type": "str"}, - "update-source": {"required": False, "type": "str"}, + "unsuppress_map": {"required": False, "type": "str"}, + "unsuppress_map6": {"required": False, "type": "str"}, + "update_source": {"required": False, "type": "str"}, "weight": {"required": False, "type": "int"} }}, - "neighbor-range": {"required": False, "type": "list", + "neighbor_range": {"required": False, "type": "list", "options": { "id": {"required": True, "type": "int"}, - "max-neighbor-num": {"required": False, "type": "int"}, - "neighbor-group": {"required": False, "type": "str"}, + "max_neighbor_num": {"required": False, "type": "int"}, + "neighbor_group": {"required": False, "type": "str"}, "prefix": {"required": False, "type": "str"} }}, + "neighbor_range6": {"required": False, "type": "list", + "options": { + "id": {"required": True, "type": "int"}, + "max_neighbor_num": {"required": False, "type": "int"}, + "neighbor_group": {"required": False, "type": "str"}, + "prefix6": {"required": False, "type": "str"} + }}, "network": {"required": False, "type": "list", "options": { "backdoor": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "id": {"required": True, "type": "int"}, "prefix": {"required": False, "type": "str"}, - "route-map": {"required": False, "type": "str"} + "route_map": {"required": False, "type": "str"} }}, - "network-import-check": {"required": False, "type": "str", + "network_import_check": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "network6": {"required": False, "type": "list", "options": { @@ -1991,24 +2287,24 @@ def main(): "choices": ["enable", "disable"]}, "id": {"required": True, "type": "int"}, "prefix6": {"required": False, "type": "str"}, - "route-map": {"required": False, "type": "str"} + "route_map": {"required": False, "type": "str"} }}, "redistribute": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"}, - "route-map": {"required": False, "type": "str"}, + "route_map": {"required": False, "type": "str"}, "status": {"required": False, "type": "str", "choices": ["enable", "disable"]} }}, "redistribute6": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"}, - "route-map": {"required": False, "type": "str"}, + "route_map": {"required": False, "type": "str"}, "status": {"required": False, "type": "str", "choices": ["enable", "disable"]} }}, - "router-id": {"required": False, "type": "str"}, - "scan-time": {"required": False, "type": "int"}, + "router_id": {"required": False, "type": "str"}, + "scan_time": {"required": False, "type": "int"}, "synchronization": {"required": False, "type": "str", "choices": ["enable", "disable"]} @@ -2018,15 +2314,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_multicast.py b/lib/ansible/modules/network/fortios/fortios_router_multicast.py index 355c75a8617..f9a49c862f4 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_multicast.py +++ b/lib/ansible/modules/network/fortios/fortios_router_multicast.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_multicast short_description: Configure router multicast in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and multicast category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,287 +41,361 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 router_multicast: description: - Configure router multicast. default: null + type: dict suboptions: interface: description: - PIM interfaces. + type: list suboptions: bfd: description: - Enable/disable Protocol Independent Multicast (PIM) Bidirectional Forwarding Detection (BFD). + type: str choices: - enable - disable - cisco-exclude-genid: + cisco_exclude_genid: description: - Exclude GenID from hello packets (compatibility with old Cisco IOS). + type: str choices: - enable - disable - dr-priority: + dr_priority: description: - DR election priority. - hello-holdtime: + type: int + hello_holdtime: description: - - Time before old neighbor information expires (0 - 65535 sec, default = 105). - hello-interval: + - Time before old neighbor information expires (0 - 65535 sec). + type: int + hello_interval: description: - - Interval between sending PIM hello messages (0 - 65535 sec, default = 30). + - Interval between sending PIM hello messages (0 - 65535 sec). + type: int igmp: description: - IGMP configuration options. + type: dict suboptions: - access-group: + access_group: description: - Groups IGMP hosts are allowed to join. Source router.access-list.name. - immediate-leave-group: + type: str + immediate_leave_group: description: - Groups to drop membership for immediately after receiving IGMPv2 leave. Source router.access-list.name. - last-member-query-count: + type: str + last_member_query_count: description: - - Number of group specific queries before removing group (2 - 7, default = 2). - last-member-query-interval: + - Number of group specific queries before removing group (2 - 7). + type: int + last_member_query_interval: description: - - Timeout between IGMPv2 leave and removing group (1 - 65535 msec, default = 1000). - query-interval: + - Timeout between IGMPv2 leave and removing group (1 - 65535 msec). + type: int + query_interval: description: - - Interval between queries to IGMP hosts (1 - 65535 sec, default = 125). - query-max-response-time: + - Interval between queries to IGMP hosts (1 - 65535 sec). + type: int + query_max_response_time: description: - - Maximum time to wait for a IGMP query response (1 - 25 sec, default = 10). - query-timeout: + - Maximum time to wait for a IGMP query response (1 - 25 sec). + type: int + query_timeout: description: - - Timeout between queries before becoming querier for network (60 - 900, default = 255). - router-alert-check: + - Timeout between queries before becoming querier for network (60 - 900). + type: int + router_alert_check: description: - Enable/disable require IGMP packets contain router alert option. + type: str choices: - enable - disable version: description: - Maximum version of IGMP to support. + type: str choices: - 3 - 2 - 1 - join-group: + join_group: description: - Join multicast groups. + type: list suboptions: address: description: - Multicast group IP address. required: true - multicast-flow: + type: str + multicast_flow: description: - Acceptable source for multicast group. Source router.multicast-flow.name. + type: str name: description: - Interface name. Source system.interface.name. required: true - neighbour-filter: + type: str + neighbour_filter: description: - Routers acknowledged as neighbor routers. Source router.access-list.name. + type: str passive: description: - Enable/disable listening to IGMP but not participating in PIM. + type: str choices: - enable - disable - pim-mode: + pim_mode: description: - PIM operation mode. + type: str choices: - sparse-mode - dense-mode - propagation-delay: + propagation_delay: description: - - Delay flooding packets on this interface (100 - 5000 msec, default = 500). - rp-candidate: + - Delay flooding packets on this interface (100 - 5000 msec). + type: int + rp_candidate: description: - Enable/disable compete to become RP in elections. + type: str choices: - enable - disable - rp-candidate-group: + rp_candidate_group: description: - Multicast groups managed by this RP. Source router.access-list.name. - rp-candidate-interval: + type: str + rp_candidate_interval: description: - - RP candidate advertisement interval (1 - 16383 sec, default = 60). - rp-candidate-priority: + - RP candidate advertisement interval (1 - 16383 sec). + type: int + rp_candidate_priority: description: - Router's priority as RP. - state-refresh-interval: + type: int + state_refresh_interval: description: - - Interval between sending state-refresh packets (1 - 100 sec, default = 60). - static-group: + - Interval between sending state-refresh packets (1 - 100 sec). + type: int + static_group: description: - Statically set multicast groups to forward out. Source router.multicast-flow.name. - ttl-threshold: + type: str + ttl_threshold: description: - - Minimum TTL of multicast packets that will be forwarded (applied only to new multicast routes) (1 - 255, default = 1). - multicast-routing: + - Minimum TTL of multicast packets that will be forwarded (applied only to new multicast routes) (1 - 255). + type: int + multicast_routing: description: - Enable/disable IP multicast routing. + type: str choices: - enable - disable - pim-sm-global: + pim_sm_global: description: - PIM sparse-mode global settings. + type: dict suboptions: - accept-register-list: + accept_register_list: description: - Sources allowed to register packets with this Rendezvous Point (RP). Source router.access-list.name. - accept-source-list: + type: str + accept_source_list: description: - Sources allowed to send multicast traffic. Source router.access-list.name. - bsr-allow-quick-refresh: + type: str + bsr_allow_quick_refresh: description: - Enable/disable accept BSR quick refresh packets from neighbors. + type: str choices: - enable - disable - bsr-candidate: + bsr_candidate: description: - Enable/disable allowing this router to become a bootstrap router (BSR). + type: str choices: - enable - disable - bsr-hash: + bsr_hash: description: - - BSR hash length (0 - 32, default = 10). - bsr-interface: + - BSR hash length (0 - 32). + type: int + bsr_interface: description: - Interface to advertise as candidate BSR. Source system.interface.name. - bsr-priority: + type: str + bsr_priority: description: - - BSR priority (0 - 255, default = 0). - cisco-crp-prefix: + - BSR priority (0 - 255). + type: int + cisco_crp_prefix: description: - Enable/disable making candidate RP compatible with old Cisco IOS. + type: str choices: - enable - disable - cisco-ignore-rp-set-priority: + cisco_ignore_rp_set_priority: description: - Use only hash for RP selection (compatibility with old Cisco IOS). + type: str choices: - enable - disable - cisco-register-checksum: + cisco_register_checksum: description: - Checksum entire register packet(for old Cisco IOS compatibility). + type: str choices: - enable - disable - cisco-register-checksum-group: + cisco_register_checksum_group: description: - Cisco register checksum only these groups. Source router.access-list.name. - join-prune-holdtime: + type: str + join_prune_holdtime: description: - - Join/prune holdtime (1 - 65535, default = 210). - message-interval: + - Join/prune holdtime (1 - 65535). + type: int + message_interval: description: - - Period of time between sending periodic PIM join/prune messages in seconds (1 - 65535, default = 60). - null-register-retries: + - Period of time between sending periodic PIM join/prune messages in seconds (1 - 65535). + type: int + null_register_retries: description: - - Maximum retries of null register (1 - 20, default = 1). - register-rate-limit: + - Maximum retries of null register (1 - 20). + type: int + register_rate_limit: description: - - Limit of packets/sec per source registered through this RP (0 - 65535, default = 0 which means unlimited). - register-rp-reachability: + - Limit of packets/sec per source registered through this RP (0 - 65535). + type: int + register_rp_reachability: description: - Enable/disable check RP is reachable before registering packets. + type: str choices: - enable - disable - register-source: + register_source: description: - Override source address in register packets. + type: str choices: - disable - interface - ip-address - register-source-interface: + register_source_interface: description: - Override with primary interface address. Source system.interface.name. - register-source-ip: + type: str + register_source_ip: description: - Override with local IP address. - register-supression: + type: str + register_supression: description: - - Period of time to honor register-stop message (1 - 65535 sec, default = 60). - rp-address: + - Period of time to honor register-stop message (1 - 65535 sec). + type: int + rp_address: description: - Statically configure RP addresses. + type: list suboptions: group: description: - Groups to use this RP. Source router.access-list.name. + type: str id: description: - ID. required: true - ip-address: + type: int + ip_address: description: - RP router address. - rp-register-keepalive: + type: str + rp_register_keepalive: description: - - Timeout for RP receiving data on (S,G) tree (1 - 65535 sec, default = 185). - spt-threshold: + - Timeout for RP receiving data on (S,G) tree (1 - 65535 sec). + type: int + spt_threshold: description: - Enable/disable switching to source specific trees. + type: str choices: - enable - disable - spt-threshold-group: + spt_threshold_group: description: - Groups allowed to switch to source tree. Source router.access-list.name. + type: str ssm: description: - Enable/disable source specific multicast. + type: str choices: - enable - disable - ssm-range: + ssm_range: description: - Groups allowed to source specific multicast. Source router.access-list.name. - route-limit: + type: str + route_limit: description: - Maximum number of multicast routes. - route-threshold: + type: int + route_threshold: description: - Generate warnings when the number of multicast routes exceeds this number, must not be greater than route-limit. + type: int ''' EXAMPLES = ''' @@ -334,6 +405,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure router multicast. fortios_router_multicast: @@ -346,70 +418,70 @@ EXAMPLES = ''' interface: - bfd: "enable" - cisco-exclude-genid: "enable" - dr-priority: "6" - hello-holdtime: "7" - hello-interval: "8" + cisco_exclude_genid: "enable" + dr_priority: "6" + hello_holdtime: "7" + hello_interval: "8" igmp: - access-group: " (source router.access-list.name)" - immediate-leave-group: " (source router.access-list.name)" - last-member-query-count: "12" - last-member-query-interval: "13" - query-interval: "14" - query-max-response-time: "15" - query-timeout: "16" - router-alert-check: "enable" + access_group: " (source router.access-list.name)" + immediate_leave_group: " (source router.access-list.name)" + last_member_query_count: "12" + last_member_query_interval: "13" + query_interval: "14" + query_max_response_time: "15" + query_timeout: "16" + router_alert_check: "enable" version: "3" - join-group: + join_group: - address: "" - multicast-flow: " (source router.multicast-flow.name)" + multicast_flow: " (source router.multicast-flow.name)" name: "default_name_22 (source system.interface.name)" - neighbour-filter: " (source router.access-list.name)" + neighbour_filter: " (source router.access-list.name)" passive: "enable" - pim-mode: "sparse-mode" - propagation-delay: "26" - rp-candidate: "enable" - rp-candidate-group: " (source router.access-list.name)" - rp-candidate-interval: "29" - rp-candidate-priority: "30" - state-refresh-interval: "31" - static-group: " (source router.multicast-flow.name)" - ttl-threshold: "33" - multicast-routing: "enable" - pim-sm-global: - accept-register-list: " (source router.access-list.name)" - accept-source-list: " (source router.access-list.name)" - bsr-allow-quick-refresh: "enable" - bsr-candidate: "enable" - bsr-hash: "40" - bsr-interface: " (source system.interface.name)" - bsr-priority: "42" - cisco-crp-prefix: "enable" - cisco-ignore-rp-set-priority: "enable" - cisco-register-checksum: "enable" - cisco-register-checksum-group: " (source router.access-list.name)" - join-prune-holdtime: "47" - message-interval: "48" - null-register-retries: "49" - register-rate-limit: "50" - register-rp-reachability: "enable" - register-source: "disable" - register-source-interface: " (source system.interface.name)" - register-source-ip: "" - register-supression: "55" - rp-address: + pim_mode: "sparse-mode" + propagation_delay: "26" + rp_candidate: "enable" + rp_candidate_group: " (source router.access-list.name)" + rp_candidate_interval: "29" + rp_candidate_priority: "30" + state_refresh_interval: "31" + static_group: " (source router.multicast-flow.name)" + ttl_threshold: "33" + multicast_routing: "enable" + pim_sm_global: + accept_register_list: " (source router.access-list.name)" + accept_source_list: " (source router.access-list.name)" + bsr_allow_quick_refresh: "enable" + bsr_candidate: "enable" + bsr_hash: "40" + bsr_interface: " (source system.interface.name)" + bsr_priority: "42" + cisco_crp_prefix: "enable" + cisco_ignore_rp_set_priority: "enable" + cisco_register_checksum: "enable" + cisco_register_checksum_group: " (source router.access-list.name)" + join_prune_holdtime: "47" + message_interval: "48" + null_register_retries: "49" + register_rate_limit: "50" + register_rp_reachability: "enable" + register_source: "disable" + register_source_interface: " (source system.interface.name)" + register_source_ip: "" + register_supression: "55" + rp_address: - group: " (source router.access-list.name)" id: "58" - ip-address: "" - rp-register-keepalive: "60" - spt-threshold: "enable" - spt-threshold-group: " (source router.access-list.name)" + ip_address: "" + rp_register_keepalive: "60" + spt_threshold: "enable" + spt_threshold_group: " (source router.access-list.name)" ssm: "enable" - ssm-range: " (source router.access-list.name)" - route-limit: "65" - route-threshold: "66" + ssm_range: " (source router.access-list.name)" + route_limit: "65" + route_threshold: "66" ''' RETURN = ''' @@ -472,14 +544,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -487,12 +561,12 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_multicast_data(json): - option_list = ['interface', 'multicast-routing', 'pim-sm-global', - 'route-limit', 'route-threshold'] + option_list = ['interface', 'multicast_routing', 'pim_sm_global', + 'route_limit', 'route_threshold'] dictionary = {} for attribute in option_list: @@ -502,17 +576,15 @@ def filter_router_multicast_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data @@ -520,125 +592,131 @@ def flatten_multilists_attributes(data): def router_multicast(data, fos): vdom = data['vdom'] router_multicast_data = data['router_multicast'] - flattened_data = flatten_multilists_attributes(router_multicast_data) - filtered_data = filter_router_multicast_data(flattened_data) + filtered_data = underscore_to_hyphen(filter_router_multicast_data(router_multicast_data)) + return fos.set('router', 'multicast', data=filtered_data, vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_multicast']: resp = router_multicast(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, "router_multicast": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { "interface": {"required": False, "type": "list", "options": { "bfd": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "cisco-exclude-genid": {"required": False, "type": "str", + "cisco_exclude_genid": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "dr-priority": {"required": False, "type": "int"}, - "hello-holdtime": {"required": False, "type": "int"}, - "hello-interval": {"required": False, "type": "int"}, + "dr_priority": {"required": False, "type": "int"}, + "hello_holdtime": {"required": False, "type": "int"}, + "hello_interval": {"required": False, "type": "int"}, "igmp": {"required": False, "type": "dict", "options": { - "access-group": {"required": False, "type": "str"}, - "immediate-leave-group": {"required": False, "type": "str"}, - "last-member-query-count": {"required": False, "type": "int"}, - "last-member-query-interval": {"required": False, "type": "int"}, - "query-interval": {"required": False, "type": "int"}, - "query-max-response-time": {"required": False, "type": "int"}, - "query-timeout": {"required": False, "type": "int"}, - "router-alert-check": {"required": False, "type": "str", + "access_group": {"required": False, "type": "str"}, + "immediate_leave_group": {"required": False, "type": "str"}, + "last_member_query_count": {"required": False, "type": "int"}, + "last_member_query_interval": {"required": False, "type": "int"}, + "query_interval": {"required": False, "type": "int"}, + "query_max_response_time": {"required": False, "type": "int"}, + "query_timeout": {"required": False, "type": "int"}, + "router_alert_check": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "version": {"required": False, "type": "str", "choices": ["3", "2", "1"]} }}, - "join-group": {"required": False, "type": "list", + "join_group": {"required": False, "type": "list", "options": { "address": {"required": True, "type": "str"} }}, - "multicast-flow": {"required": False, "type": "str"}, + "multicast_flow": {"required": False, "type": "str"}, "name": {"required": True, "type": "str"}, - "neighbour-filter": {"required": False, "type": "str"}, + "neighbour_filter": {"required": False, "type": "str"}, "passive": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "pim-mode": {"required": False, "type": "str", + "pim_mode": {"required": False, "type": "str", "choices": ["sparse-mode", "dense-mode"]}, - "propagation-delay": {"required": False, "type": "int"}, - "rp-candidate": {"required": False, "type": "str", + "propagation_delay": {"required": False, "type": "int"}, + "rp_candidate": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "rp-candidate-group": {"required": False, "type": "str"}, - "rp-candidate-interval": {"required": False, "type": "int"}, - "rp-candidate-priority": {"required": False, "type": "int"}, - "state-refresh-interval": {"required": False, "type": "int"}, - "static-group": {"required": False, "type": "str"}, - "ttl-threshold": {"required": False, "type": "int"} + "rp_candidate_group": {"required": False, "type": "str"}, + "rp_candidate_interval": {"required": False, "type": "int"}, + "rp_candidate_priority": {"required": False, "type": "int"}, + "state_refresh_interval": {"required": False, "type": "int"}, + "static_group": {"required": False, "type": "str"}, + "ttl_threshold": {"required": False, "type": "int"} }}, - "multicast-routing": {"required": False, "type": "str", + "multicast_routing": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "pim-sm-global": {"required": False, "type": "dict", + "pim_sm_global": {"required": False, "type": "dict", "options": { - "accept-register-list": {"required": False, "type": "str"}, - "accept-source-list": {"required": False, "type": "str"}, - "bsr-allow-quick-refresh": {"required": False, "type": "str", + "accept_register_list": {"required": False, "type": "str"}, + "accept_source_list": {"required": False, "type": "str"}, + "bsr_allow_quick_refresh": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "bsr-candidate": {"required": False, "type": "str", + "bsr_candidate": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "bsr-hash": {"required": False, "type": "int"}, - "bsr-interface": {"required": False, "type": "str"}, - "bsr-priority": {"required": False, "type": "int"}, - "cisco-crp-prefix": {"required": False, "type": "str", + "bsr_hash": {"required": False, "type": "int"}, + "bsr_interface": {"required": False, "type": "str"}, + "bsr_priority": {"required": False, "type": "int"}, + "cisco_crp_prefix": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "cisco-ignore-rp-set-priority": {"required": False, "type": "str", + "cisco_ignore_rp_set_priority": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "cisco-register-checksum": {"required": False, "type": "str", + "cisco_register_checksum": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "cisco-register-checksum-group": {"required": False, "type": "str"}, - "join-prune-holdtime": {"required": False, "type": "int"}, - "message-interval": {"required": False, "type": "int"}, - "null-register-retries": {"required": False, "type": "int"}, - "register-rate-limit": {"required": False, "type": "int"}, - "register-rp-reachability": {"required": False, "type": "str", + "cisco_register_checksum_group": {"required": False, "type": "str"}, + "join_prune_holdtime": {"required": False, "type": "int"}, + "message_interval": {"required": False, "type": "int"}, + "null_register_retries": {"required": False, "type": "int"}, + "register_rate_limit": {"required": False, "type": "int"}, + "register_rp_reachability": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "register-source": {"required": False, "type": "str", + "register_source": {"required": False, "type": "str", "choices": ["disable", "interface", "ip-address"]}, - "register-source-interface": {"required": False, "type": "str"}, - "register-source-ip": {"required": False, "type": "str"}, - "register-supression": {"required": False, "type": "int"}, - "rp-address": {"required": False, "type": "list", + "register_source_interface": {"required": False, "type": "str"}, + "register_source_ip": {"required": False, "type": "str"}, + "register_supression": {"required": False, "type": "int"}, + "rp_address": {"required": False, "type": "list", "options": { "group": {"required": False, "type": "str"}, "id": {"required": True, "type": "int"}, - "ip-address": {"required": False, "type": "str"} + "ip_address": {"required": False, "type": "str"} }}, - "rp-register-keepalive": {"required": False, "type": "int"}, - "spt-threshold": {"required": False, "type": "str", + "rp_register_keepalive": {"required": False, "type": "int"}, + "spt_threshold": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "spt-threshold-group": {"required": False, "type": "str"}, + "spt_threshold_group": {"required": False, "type": "str"}, "ssm": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ssm-range": {"required": False, "type": "str"} + "ssm_range": {"required": False, "type": "str"} }}, - "route-limit": {"required": False, "type": "int"}, - "route-threshold": {"required": False, "type": "int"} + "route_limit": {"required": False, "type": "int"}, + "route_threshold": {"required": False, "type": "int"} } } @@ -646,15 +724,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_multicast6.py b/lib/ansible/modules/network/fortios/fortios_router_multicast6.py index bcf3cb81d47..7224d8642e2 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_multicast6.py +++ b/lib/ansible/modules/network/fortios/fortios_router_multicast6.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_multicast6 short_description: Configure IPv6 multicast in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and multicast6 category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,78 +41,99 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 router_multicast6: description: - Configure IPv6 multicast. default: null + type: dict suboptions: interface: description: - Protocol Independent Multicast (PIM) interfaces. + type: list suboptions: - hello-holdtime: + hello_holdtime: description: - - Time before old neighbour information expires (1 - 65535 sec, default = 105). - hello-interval: + - Time before old neighbour information expires (1 - 65535 sec). + type: int + hello_interval: description: - - Interval between sending PIM hello messages (1 - 65535 sec, default = 30).. + - Interval between sending PIM hello messages (1 - 65535 sec).. + type: int name: description: - Interface name. Source system.interface.name. required: true - multicast-pmtu: + type: str + multicast_pmtu: description: - Enable/disable PMTU for IPv6 multicast. + type: str choices: - enable - disable - multicast-routing: + multicast_routing: description: - Enable/disable IPv6 multicast routing. + type: str choices: - enable - disable - pim-sm-global: + pim_sm_global: description: - PIM sparse-mode global settings. + type: dict suboptions: - register-rate-limit: + register_rate_limit: description: - Limit of packets/sec per source registered through this RP (0 means unlimited). - rp-address: + type: int + rp_address: description: - Statically configured RP addresses. + type: list suboptions: id: description: - ID of the entry. required: true - ip6-address: + type: int + ip6_address: description: - RP router IPv6 address. + type: str ''' EXAMPLES = ''' @@ -125,6 +143,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPv6 multicast. fortios_router_multicast6: @@ -136,17 +155,17 @@ EXAMPLES = ''' router_multicast6: interface: - - hello-holdtime: "4" - hello-interval: "5" + hello_holdtime: "4" + hello_interval: "5" name: "default_name_6 (source system.interface.name)" - multicast-pmtu: "enable" - multicast-routing: "enable" - pim-sm-global: - register-rate-limit: "10" - rp-address: + multicast_pmtu: "enable" + multicast_routing: "enable" + pim_sm_global: + register_rate_limit: "10" + rp_address: - id: "12" - ip6-address: "" + ip6_address: "" ''' RETURN = ''' @@ -209,14 +228,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -224,12 +245,12 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_multicast6_data(json): - option_list = ['interface', 'multicast-pmtu', 'multicast-routing', - 'pim-sm-global'] + option_list = ['interface', 'multicast_pmtu', 'multicast_routing', + 'pim_sm_global'] dictionary = {} for attribute in option_list: @@ -239,17 +260,15 @@ def filter_router_multicast6_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data @@ -257,51 +276,57 @@ def flatten_multilists_attributes(data): def router_multicast6(data, fos): vdom = data['vdom'] router_multicast6_data = data['router_multicast6'] - flattened_data = flatten_multilists_attributes(router_multicast6_data) - filtered_data = filter_router_multicast6_data(flattened_data) + filtered_data = underscore_to_hyphen(filter_router_multicast6_data(router_multicast6_data)) + return fos.set('router', 'multicast6', data=filtered_data, vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_multicast6']: resp = router_multicast6(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, "router_multicast6": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { "interface": {"required": False, "type": "list", "options": { - "hello-holdtime": {"required": False, "type": "int"}, - "hello-interval": {"required": False, "type": "int"}, + "hello_holdtime": {"required": False, "type": "int"}, + "hello_interval": {"required": False, "type": "int"}, "name": {"required": True, "type": "str"} }}, - "multicast-pmtu": {"required": False, "type": "str", + "multicast_pmtu": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "multicast-routing": {"required": False, "type": "str", + "multicast_routing": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "pim-sm-global": {"required": False, "type": "dict", + "pim_sm_global": {"required": False, "type": "dict", "options": { - "register-rate-limit": {"required": False, "type": "int"}, - "rp-address": {"required": False, "type": "list", + "register_rate_limit": {"required": False, "type": "int"}, + "rp_address": {"required": False, "type": "list", "options": { "id": {"required": True, "type": "int"}, - "ip6-address": {"required": False, "type": "str"} + "ip6_address": {"required": False, "type": "str"} }} }} @@ -311,15 +336,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_multicast_flow.py b/lib/ansible/modules/network/fortios/fortios_router_multicast_flow.py index 0b4d7ef310b..1101fb3bd68 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_multicast_flow.py +++ b/lib/ansible/modules/network/fortios/fortios_router_multicast_flow.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_multicast_flow short_description: Configure multicast-flow in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and multicast_flow category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,61 +41,80 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 router_multicast_flow: description: - Configure multicast-flow. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent comments: description: - Comment. + type: str flows: description: - Multicast-flow entries. + type: list suboptions: - group-addr: + group_addr: description: - Multicast group IP address. + type: str id: description: - Flow ID. required: true - source-addr: + type: int + source_addr: description: - Multicast source IP address. + type: str name: description: - Name. required: true + type: str ''' EXAMPLES = ''' @@ -108,6 +124,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure multicast-flow. fortios_router_multicast_flow: @@ -116,14 +133,14 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" router_multicast_flow: - state: "present" comments: "" flows: - - group-addr: "" + group_addr: "" id: "6" - source-addr: "" + source_addr: "" name: "default_name_8" ''' @@ -187,14 +204,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -202,7 +221,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_multicast_flow_data(json): @@ -216,67 +235,72 @@ def filter_router_multicast_flow_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data def router_multicast_flow(data, fos): vdom = data['vdom'] + state = data['state'] router_multicast_flow_data = data['router_multicast_flow'] - flattened_data = flatten_multilists_attributes(router_multicast_flow_data) - filtered_data = filter_router_multicast_flow_data(flattened_data) - if router_multicast_flow_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_router_multicast_flow_data(router_multicast_flow_data)) + + if state == "present": return fos.set('router', 'multicast-flow', data=filtered_data, vdom=vdom) - elif router_multicast_flow_data['state'] == "absent": + elif state == "absent": return fos.delete('router', 'multicast-flow', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_multicast_flow']: resp = router_multicast_flow(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "router_multicast_flow": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "comments": {"required": False, "type": "str"}, "flows": {"required": False, "type": "list", "options": { - "group-addr": {"required": False, "type": "str"}, + "group_addr": {"required": False, "type": "str"}, "id": {"required": True, "type": "int"}, - "source-addr": {"required": False, "type": "str"} + "source_addr": {"required": False, "type": "str"} }}, "name": {"required": True, "type": "str"} @@ -286,15 +310,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_ospf.py b/lib/ansible/modules/network/fortios/fortios_router_ospf.py index 62e2964786d..67f01aafe84 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_ospf.py +++ b/lib/ansible/modules/network/fortios/fortios_router_ospf.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_ospf short_description: Configure OSPF in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and ospf category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,37 +41,48 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 router_ospf: description: - Configure OSPF. default: null + type: dict suboptions: - abr-type: + abr_type: description: - Area border router type. + type: str choices: - cisco - ibm @@ -83,24 +91,29 @@ options: area: description: - OSPF area configuration. + type: list suboptions: authentication: description: - Authentication type. + type: str choices: - none - text - md5 - default-cost: + default_cost: description: - Summary default cost of stub or NSSA area. - filter-list: + type: int + filter_list: description: - OSPF area filter-list configuration. + type: list suboptions: direction: description: - Direction. + type: str choices: - in - out @@ -108,38 +121,46 @@ options: description: - Filter list entry ID. required: true + type: int list: description: - Access-list or prefix-list name. Source router.access-list.name router.prefix-list.name. + type: str id: description: - Area entry IP address. required: true - nssa-default-information-originate: + type: str + nssa_default_information_originate: description: - Redistribute, advertise, or do not originate Type-7 default route into NSSA area. + type: str choices: - enable - always - disable - nssa-default-information-originate-metric: + nssa_default_information_originate_metric: description: - OSPF default metric. - nssa-default-information-originate-metric-type: + type: int + nssa_default_information_originate_metric_type: description: - OSPF metric type for default routes. + type: str choices: - 1 - 2 - nssa-redistribution: + nssa_redistribution: description: - Enable/disable redistribute into NSSA area. + type: str choices: - enable - disable - nssa-translator-role: + nssa_translator_role: description: - NSSA translator role type. + type: str choices: - candidate - never @@ -147,10 +168,12 @@ options: range: description: - OSPF area range configuration. + type: list suboptions: advertise: description: - Enable/disable advertise status. + type: str choices: - disable - enable @@ -158,210 +181,262 @@ options: description: - Range entry ID. required: true + type: int prefix: description: - Prefix. + type: str substitute: description: - Substitute prefix. - substitute-status: + type: str + substitute_status: description: - Enable/disable substitute status. + type: str choices: - enable - disable shortcut: description: - Enable/disable shortcut option. + type: str choices: - disable - enable - default - stub-type: + stub_type: description: - Stub summary setting. + type: str choices: - no-summary - summary type: description: - Area type setting. + type: str choices: - regular - nssa - stub - virtual-link: + virtual_link: description: - OSPF virtual link configuration. + type: list suboptions: authentication: description: - Authentication type. + type: str choices: - none - text - md5 - authentication-key: + authentication_key: description: - Authentication key. - dead-interval: + type: str + dead_interval: description: - Dead interval. - hello-interval: + type: int + hello_interval: description: - Hello interval. - md5-key: + type: int + md5_key: description: - MD5 key. + type: str name: description: - Virtual link entry name. required: true + type: str peer: description: - Peer IP. - retransmit-interval: + type: str + retransmit_interval: description: - Retransmit interval. - transmit-delay: + type: int + transmit_delay: description: - Transmit delay. - auto-cost-ref-bandwidth: + type: int + auto_cost_ref_bandwidth: description: - Reference bandwidth in terms of megabits per second. + type: int bfd: description: - Bidirectional Forwarding Detection (BFD). + type: str choices: - enable - disable - database-overflow: + database_overflow: description: - Enable/disable database overflow. + type: str choices: - enable - disable - database-overflow-max-lsas: + database_overflow_max_lsas: description: - Database overflow maximum LSAs. - database-overflow-time-to-recover: + type: int + database_overflow_time_to_recover: description: - Database overflow time to recover (sec). - default-information-metric: + type: int + default_information_metric: description: - Default information metric. - default-information-metric-type: + type: int + default_information_metric_type: description: - Default information metric type. + type: str choices: - 1 - 2 - default-information-originate: + default_information_originate: description: - Enable/disable generation of default route. + type: str choices: - enable - always - disable - default-information-route-map: + default_information_route_map: description: - Default information route map. Source router.route-map.name. - default-metric: + type: str + default_metric: description: - Default metric of redistribute routes. + type: int distance: description: - Distance of the route. - distance-external: + type: int + distance_external: description: - Administrative external distance. - distance-inter-area: + type: int + distance_inter_area: description: - Administrative inter-area distance. - distance-intra-area: + type: int + distance_intra_area: description: - Administrative intra-area distance. - distribute-list: + type: int + distribute_list: description: - Distribute list configuration. + type: list suboptions: - access-list: + access_list: description: - Access list name. Source router.access-list.name. + type: str id: description: - Distribute list entry ID. required: true + type: int protocol: description: - Protocol type. + type: str choices: - connected - static - rip - distribute-list-in: + distribute_list_in: description: - Filter incoming routes. Source router.access-list.name router.prefix-list.name. - distribute-route-map-in: + type: str + distribute_route_map_in: description: - Filter incoming external routes by route-map. Source router.route-map.name. - log-neighbour-changes: + type: str + log_neighbour_changes: description: - Enable logging of OSPF neighbour's changes + type: str choices: - enable - disable neighbor: description: - OSPF neighbor configuration are used when OSPF runs on non-broadcast media + type: list suboptions: cost: description: - Cost of the interface, value range from 0 to 65535, 0 means auto-cost. + type: int id: description: - Neighbor entry ID. required: true + type: int ip: description: - Interface IP address of the neighbor. - poll-interval: + type: str + poll_interval: description: - Poll interval time in seconds. + type: int priority: description: - Priority. + type: int network: description: - OSPF network configuration. + type: list suboptions: area: description: - Attach the network to area. + type: str id: description: - Network entry ID. required: true + type: int prefix: description: - Prefix. - ospf-interface: + type: str + ospf_interface: description: - OSPF interface configuration. + type: list suboptions: authentication: description: - Authentication type. + type: str choices: - none - text - md5 - authentication-key: + authentication_key: description: - Authentication key. + type: str bfd: description: - Bidirectional Forwarding Detection (BFD). + type: str choices: - global - enable @@ -369,36 +444,46 @@ options: cost: description: - Cost of the interface, value range from 0 to 65535, 0 means auto-cost. - database-filter-out: + type: int + database_filter_out: description: - Enable/disable control of flooding out LSAs. + type: str choices: - enable - disable - dead-interval: + dead_interval: description: - Dead interval. - hello-interval: + type: int + hello_interval: description: - Hello interval. - hello-multiplier: + type: int + hello_multiplier: description: - Number of hello packets within dead interval. + type: int interface: description: - Configuration interface name. Source system.interface.name. + type: str ip: description: - IP address. - md5-key: + type: str + md5_key: description: - MD5 key. + type: str mtu: description: - MTU for database description packets. - mtu-ignore: + type: int + mtu_ignore: description: - Enable/disable ignore MTU. + type: str choices: - enable - disable @@ -406,54 +491,67 @@ options: description: - Interface entry name. required: true - network-type: + type: str + network_type: description: - Network type. + type: str choices: - broadcast - non-broadcast - point-to-point - point-to-multipoint - point-to-multipoint-non-broadcast - prefix-length: + prefix_length: description: - Prefix length. + type: int priority: description: - Priority. - resync-timeout: + type: int + resync_timeout: description: - Graceful restart neighbor resynchronization timeout. - retransmit-interval: + type: int + retransmit_interval: description: - Retransmit interval. + type: int status: description: - Enable/disable status. + type: str choices: - disable - enable - transmit-delay: + transmit_delay: description: - Transmit delay. - passive-interface: + type: int + passive_interface: description: - Passive interface configuration. + type: list suboptions: name: description: - Passive interface name. Source system.interface.name. required: true + type: str redistribute: description: - Redistribute configuration. + type: list suboptions: metric: description: - Redistribute metric setting. - metric-type: + type: int + metric_type: description: - Metric type. + type: str choices: - 1 - 2 @@ -461,47 +559,58 @@ options: description: - Redistribute name. required: true + type: str routemap: description: - Route map name. Source router.route-map.name. + type: str status: description: - status + type: str choices: - enable - disable tag: description: - Tag value. - restart-mode: + type: int + restart_mode: description: - OSPF restart mode (graceful or LLS). + type: str choices: - none - lls - graceful-restart - restart-period: + restart_period: description: - Graceful restart period. - rfc1583-compatible: + type: int + rfc1583_compatible: description: - Enable/disable RFC1583 compatibility. + type: str choices: - enable - disable - router-id: + router_id: description: - Router ID. - spf-timers: + type: str + spf_timers: description: - SPF calculation frequency. - summary-address: + type: str + summary_address: description: - IP address summary configuration. + type: list suboptions: advertise: description: - Enable/disable advertise status. + type: str choices: - disable - enable @@ -509,12 +618,15 @@ options: description: - Summary address entry ID. required: true + type: int prefix: description: - Prefix. + type: str tag: description: - Tag value. + type: int ''' EXAMPLES = ''' @@ -524,6 +636,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure OSPF. fortios_router_ospf: @@ -533,117 +646,117 @@ EXAMPLES = ''' vdom: "{{ vdom }}" https: "False" router_ospf: - abr-type: "cisco" + abr_type: "cisco" area: - authentication: "none" - default-cost: "6" - filter-list: + default_cost: "6" + filter_list: - direction: "in" id: "9" list: " (source router.access-list.name router.prefix-list.name)" id: "11" - nssa-default-information-originate: "enable" - nssa-default-information-originate-metric: "13" - nssa-default-information-originate-metric-type: "1" - nssa-redistribution: "enable" - nssa-translator-role: "candidate" + nssa_default_information_originate: "enable" + nssa_default_information_originate_metric: "13" + nssa_default_information_originate_metric_type: "1" + nssa_redistribution: "enable" + nssa_translator_role: "candidate" range: - advertise: "disable" id: "19" prefix: "" substitute: "" - substitute-status: "enable" + substitute_status: "enable" shortcut: "disable" - stub-type: "no-summary" + stub_type: "no-summary" type: "regular" - virtual-link: + virtual_link: - authentication: "none" - authentication-key: "" - dead-interval: "29" - hello-interval: "30" - md5-key: "" + authentication_key: "" + dead_interval: "29" + hello_interval: "30" + md5_key: "" name: "default_name_32" peer: "" - retransmit-interval: "34" - transmit-delay: "35" - auto-cost-ref-bandwidth: "36" + retransmit_interval: "34" + transmit_delay: "35" + auto_cost_ref_bandwidth: "36" bfd: "enable" - database-overflow: "enable" - database-overflow-max-lsas: "39" - database-overflow-time-to-recover: "40" - default-information-metric: "41" - default-information-metric-type: "1" - default-information-originate: "enable" - default-information-route-map: " (source router.route-map.name)" - default-metric: "45" + database_overflow: "enable" + database_overflow_max_lsas: "39" + database_overflow_time_to_recover: "40" + default_information_metric: "41" + default_information_metric_type: "1" + default_information_originate: "enable" + default_information_route_map: " (source router.route-map.name)" + default_metric: "45" distance: "46" - distance-external: "47" - distance-inter-area: "48" - distance-intra-area: "49" - distribute-list: + distance_external: "47" + distance_inter_area: "48" + distance_intra_area: "49" + distribute_list: - - access-list: " (source router.access-list.name)" + access_list: " (source router.access-list.name)" id: "52" protocol: "connected" - distribute-list-in: " (source router.access-list.name router.prefix-list.name)" - distribute-route-map-in: " (source router.route-map.name)" - log-neighbour-changes: "enable" + distribute_list_in: " (source router.access-list.name router.prefix-list.name)" + distribute_route_map_in: " (source router.route-map.name)" + log_neighbour_changes: "enable" neighbor: - cost: "58" id: "59" ip: "" - poll-interval: "61" + poll_interval: "61" priority: "62" network: - area: "" id: "65" prefix: "" - ospf-interface: + ospf_interface: - authentication: "none" - authentication-key: "" + authentication_key: "" bfd: "global" cost: "71" - database-filter-out: "enable" - dead-interval: "73" - hello-interval: "74" - hello-multiplier: "75" + database_filter_out: "enable" + dead_interval: "73" + hello_interval: "74" + hello_multiplier: "75" interface: " (source system.interface.name)" ip: "" - md5-key: "" + md5_key: "" mtu: "79" - mtu-ignore: "enable" + mtu_ignore: "enable" name: "default_name_81" - network-type: "broadcast" - prefix-length: "83" + network_type: "broadcast" + prefix_length: "83" priority: "84" - resync-timeout: "85" - retransmit-interval: "86" + resync_timeout: "85" + retransmit_interval: "86" status: "disable" - transmit-delay: "88" - passive-interface: + transmit_delay: "88" + passive_interface: - name: "default_name_90 (source system.interface.name)" redistribute: - metric: "92" - metric-type: "1" + metric_type: "1" name: "default_name_94" routemap: " (source router.route-map.name)" status: "enable" tag: "97" - restart-mode: "none" - restart-period: "99" - rfc1583-compatible: "enable" - router-id: "" - spf-timers: "" - summary-address: + restart_mode: "none" + restart_period: "99" + rfc1583_compatible: "enable" + router_id: "" + spf_timers: "" + summary_address: - advertise: "disable" id: "105" @@ -711,14 +824,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -726,21 +841,21 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_ospf_data(json): - option_list = ['abr-type', 'area', 'auto-cost-ref-bandwidth', - 'bfd', 'database-overflow', 'database-overflow-max-lsas', - 'database-overflow-time-to-recover', 'default-information-metric', 'default-information-metric-type', - 'default-information-originate', 'default-information-route-map', 'default-metric', - 'distance', 'distance-external', 'distance-inter-area', - 'distance-intra-area', 'distribute-list', 'distribute-list-in', - 'distribute-route-map-in', 'log-neighbour-changes', 'neighbor', - 'network', 'ospf-interface', 'passive-interface', - 'redistribute', 'restart-mode', 'restart-period', - 'rfc1583-compatible', 'router-id', 'spf-timers', - 'summary-address'] + option_list = ['abr_type', 'area', 'auto_cost_ref_bandwidth', + 'bfd', 'database_overflow', 'database_overflow_max_lsas', + 'database_overflow_time_to_recover', 'default_information_metric', 'default_information_metric_type', + 'default_information_originate', 'default_information_route_map', 'default_metric', + 'distance', 'distance_external', 'distance_inter_area', + 'distance_intra_area', 'distribute_list', 'distribute_list_in', + 'distribute_route_map_in', 'log_neighbour_changes', 'neighbor', + 'network', 'ospf_interface', 'passive_interface', + 'redistribute', 'restart_mode', 'restart_period', + 'rfc1583_compatible', 'router_id', 'spf_timers', + 'summary_address'] dictionary = {} for attribute in option_list: @@ -750,17 +865,15 @@ def filter_router_ospf_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data @@ -768,43 +881,49 @@ def flatten_multilists_attributes(data): def router_ospf(data, fos): vdom = data['vdom'] router_ospf_data = data['router_ospf'] - flattened_data = flatten_multilists_attributes(router_ospf_data) - filtered_data = filter_router_ospf_data(flattened_data) + filtered_data = underscore_to_hyphen(filter_router_ospf_data(router_ospf_data)) + return fos.set('router', 'ospf', data=filtered_data, vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_ospf']: resp = router_ospf(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, "router_ospf": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "abr-type": {"required": False, "type": "str", + "abr_type": {"required": False, "type": "str", "choices": ["cisco", "ibm", "shortcut", "standard"]}, "area": {"required": False, "type": "list", "options": { "authentication": {"required": False, "type": "str", "choices": ["none", "text", "md5"]}, - "default-cost": {"required": False, "type": "int"}, - "filter-list": {"required": False, "type": "list", + "default_cost": {"required": False, "type": "int"}, + "filter_list": {"required": False, "type": "list", "options": { "direction": {"required": False, "type": "str", "choices": ["in", "out"]}, @@ -812,14 +931,14 @@ def main(): "list": {"required": False, "type": "str"} }}, "id": {"required": True, "type": "str"}, - "nssa-default-information-originate": {"required": False, "type": "str", + "nssa_default_information_originate": {"required": False, "type": "str", "choices": ["enable", "always", "disable"]}, - "nssa-default-information-originate-metric": {"required": False, "type": "int"}, - "nssa-default-information-originate-metric-type": {"required": False, "type": "str", + "nssa_default_information_originate_metric": {"required": False, "type": "int"}, + "nssa_default_information_originate_metric_type": {"required": False, "type": "str", "choices": ["1", "2"]}, - "nssa-redistribution": {"required": False, "type": "str", + "nssa_redistribution": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "nssa-translator-role": {"required": False, "type": "str", + "nssa_translator_role": {"required": False, "type": "str", "choices": ["candidate", "never", "always"]}, "range": {"required": False, "type": "list", "options": { @@ -828,64 +947,64 @@ def main(): "id": {"required": True, "type": "int"}, "prefix": {"required": False, "type": "str"}, "substitute": {"required": False, "type": "str"}, - "substitute-status": {"required": False, "type": "str", + "substitute_status": {"required": False, "type": "str", "choices": ["enable", "disable"]} }}, "shortcut": {"required": False, "type": "str", "choices": ["disable", "enable", "default"]}, - "stub-type": {"required": False, "type": "str", + "stub_type": {"required": False, "type": "str", "choices": ["no-summary", "summary"]}, "type": {"required": False, "type": "str", "choices": ["regular", "nssa", "stub"]}, - "virtual-link": {"required": False, "type": "list", + "virtual_link": {"required": False, "type": "list", "options": { "authentication": {"required": False, "type": "str", "choices": ["none", "text", "md5"]}, - "authentication-key": {"required": False, "type": "str"}, - "dead-interval": {"required": False, "type": "int"}, - "hello-interval": {"required": False, "type": "int"}, - "md5-key": {"required": False, "type": "str"}, + "authentication_key": {"required": False, "type": "str"}, + "dead_interval": {"required": False, "type": "int"}, + "hello_interval": {"required": False, "type": "int"}, + "md5_key": {"required": False, "type": "str"}, "name": {"required": True, "type": "str"}, "peer": {"required": False, "type": "str"}, - "retransmit-interval": {"required": False, "type": "int"}, - "transmit-delay": {"required": False, "type": "int"} + "retransmit_interval": {"required": False, "type": "int"}, + "transmit_delay": {"required": False, "type": "int"} }} }}, - "auto-cost-ref-bandwidth": {"required": False, "type": "int"}, + "auto_cost_ref_bandwidth": {"required": False, "type": "int"}, "bfd": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "database-overflow": {"required": False, "type": "str", + "database_overflow": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "database-overflow-max-lsas": {"required": False, "type": "int"}, - "database-overflow-time-to-recover": {"required": False, "type": "int"}, - "default-information-metric": {"required": False, "type": "int"}, - "default-information-metric-type": {"required": False, "type": "str", + "database_overflow_max_lsas": {"required": False, "type": "int"}, + "database_overflow_time_to_recover": {"required": False, "type": "int"}, + "default_information_metric": {"required": False, "type": "int"}, + "default_information_metric_type": {"required": False, "type": "str", "choices": ["1", "2"]}, - "default-information-originate": {"required": False, "type": "str", + "default_information_originate": {"required": False, "type": "str", "choices": ["enable", "always", "disable"]}, - "default-information-route-map": {"required": False, "type": "str"}, - "default-metric": {"required": False, "type": "int"}, + "default_information_route_map": {"required": False, "type": "str"}, + "default_metric": {"required": False, "type": "int"}, "distance": {"required": False, "type": "int"}, - "distance-external": {"required": False, "type": "int"}, - "distance-inter-area": {"required": False, "type": "int"}, - "distance-intra-area": {"required": False, "type": "int"}, - "distribute-list": {"required": False, "type": "list", + "distance_external": {"required": False, "type": "int"}, + "distance_inter_area": {"required": False, "type": "int"}, + "distance_intra_area": {"required": False, "type": "int"}, + "distribute_list": {"required": False, "type": "list", "options": { - "access-list": {"required": False, "type": "str"}, + "access_list": {"required": False, "type": "str"}, "id": {"required": True, "type": "int"}, "protocol": {"required": False, "type": "str", "choices": ["connected", "static", "rip"]} }}, - "distribute-list-in": {"required": False, "type": "str"}, - "distribute-route-map-in": {"required": False, "type": "str"}, - "log-neighbour-changes": {"required": False, "type": "str", + "distribute_list_in": {"required": False, "type": "str"}, + "distribute_route_map_in": {"required": False, "type": "str"}, + "log_neighbour_changes": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "neighbor": {"required": False, "type": "list", "options": { "cost": {"required": False, "type": "int"}, "id": {"required": True, "type": "int"}, "ip": {"required": False, "type": "str"}, - "poll-interval": {"required": False, "type": "int"}, + "poll_interval": {"required": False, "type": "int"}, "priority": {"required": False, "type": "int"} }}, "network": {"required": False, "type": "list", @@ -894,45 +1013,45 @@ def main(): "id": {"required": True, "type": "int"}, "prefix": {"required": False, "type": "str"} }}, - "ospf-interface": {"required": False, "type": "list", + "ospf_interface": {"required": False, "type": "list", "options": { "authentication": {"required": False, "type": "str", "choices": ["none", "text", "md5"]}, - "authentication-key": {"required": False, "type": "str"}, + "authentication_key": {"required": False, "type": "str"}, "bfd": {"required": False, "type": "str", "choices": ["global", "enable", "disable"]}, "cost": {"required": False, "type": "int"}, - "database-filter-out": {"required": False, "type": "str", + "database_filter_out": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "dead-interval": {"required": False, "type": "int"}, - "hello-interval": {"required": False, "type": "int"}, - "hello-multiplier": {"required": False, "type": "int"}, + "dead_interval": {"required": False, "type": "int"}, + "hello_interval": {"required": False, "type": "int"}, + "hello_multiplier": {"required": False, "type": "int"}, "interface": {"required": False, "type": "str"}, "ip": {"required": False, "type": "str"}, - "md5-key": {"required": False, "type": "str"}, + "md5_key": {"required": False, "type": "str"}, "mtu": {"required": False, "type": "int"}, - "mtu-ignore": {"required": False, "type": "str", + "mtu_ignore": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "name": {"required": True, "type": "str"}, - "network-type": {"required": False, "type": "str", + "network_type": {"required": False, "type": "str", "choices": ["broadcast", "non-broadcast", "point-to-point", "point-to-multipoint", "point-to-multipoint-non-broadcast"]}, - "prefix-length": {"required": False, "type": "int"}, + "prefix_length": {"required": False, "type": "int"}, "priority": {"required": False, "type": "int"}, - "resync-timeout": {"required": False, "type": "int"}, - "retransmit-interval": {"required": False, "type": "int"}, + "resync_timeout": {"required": False, "type": "int"}, + "retransmit_interval": {"required": False, "type": "int"}, "status": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "transmit-delay": {"required": False, "type": "int"} + "transmit_delay": {"required": False, "type": "int"} }}, - "passive-interface": {"required": False, "type": "list", + "passive_interface": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, "redistribute": {"required": False, "type": "list", "options": { "metric": {"required": False, "type": "int"}, - "metric-type": {"required": False, "type": "str", + "metric_type": {"required": False, "type": "str", "choices": ["1", "2"]}, "name": {"required": True, "type": "str"}, "routemap": {"required": False, "type": "str"}, @@ -940,14 +1059,14 @@ def main(): "choices": ["enable", "disable"]}, "tag": {"required": False, "type": "int"} }}, - "restart-mode": {"required": False, "type": "str", + "restart_mode": {"required": False, "type": "str", "choices": ["none", "lls", "graceful-restart"]}, - "restart-period": {"required": False, "type": "int"}, - "rfc1583-compatible": {"required": False, "type": "str", + "restart_period": {"required": False, "type": "int"}, + "rfc1583_compatible": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "router-id": {"required": False, "type": "str"}, - "spf-timers": {"required": False, "type": "str"}, - "summary-address": {"required": False, "type": "list", + "router_id": {"required": False, "type": "str"}, + "spf_timers": {"required": False, "type": "str"}, + "summary_address": {"required": False, "type": "list", "options": { "advertise": {"required": False, "type": "str", "choices": ["disable", "enable"]}, @@ -962,15 +1081,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_ospf6.py b/lib/ansible/modules/network/fortios/fortios_router_ospf6.py index 6cc0286d66a..d06340c5a28 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_ospf6.py +++ b/lib/ansible/modules/network/fortios/fortios_router_ospf6.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_ospf6 short_description: Configure IPv6 OSPF in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and ospf6 category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,37 +41,48 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 router_ospf6: description: - Configure IPv6 OSPF. default: null + type: dict suboptions: - abr-type: + abr_type: description: - Area border router type. + type: str choices: - cisco - ibm @@ -82,38 +90,46 @@ options: area: description: - OSPF6 area configuration. + type: list suboptions: - default-cost: + default_cost: description: - Summary default cost of stub or NSSA area. + type: int id: description: - Area entry IP address. required: true - nssa-default-information-originate: + type: str + nssa_default_information_originate: description: - Enable/disable originate type 7 default into NSSA area. + type: str choices: - enable - disable - nssa-default-information-originate-metric: + nssa_default_information_originate_metric: description: - OSPFv3 default metric. - nssa-default-information-originate-metric-type: + type: int + nssa_default_information_originate_metric_type: description: - OSPFv3 metric type for default routes. + type: str choices: - 1 - 2 - nssa-redistribution: + nssa_redistribution: description: - Enable/disable redistribute into NSSA area. + type: str choices: - enable - disable - nssa-translator-role: + nssa_translator_role: description: - NSSA translator role type. + type: str choices: - candidate - never @@ -121,10 +137,12 @@ options: range: description: - OSPF6 area range configuration. + type: list suboptions: advertise: description: - Enable/disable advertise status. + type: str choices: - disable - enable @@ -132,92 +150,114 @@ options: description: - Range entry ID. required: true + type: int prefix6: description: - IPv6 prefix. - stub-type: + type: str + stub_type: description: - Stub summary setting. + type: str choices: - no-summary - summary type: description: - Area type setting. + type: str choices: - regular - nssa - stub - virtual-link: + virtual_link: description: - OSPF6 virtual link configuration. + type: list suboptions: - dead-interval: + dead_interval: description: - Dead interval. - hello-interval: + type: int + hello_interval: description: - Hello interval. + type: int name: description: - Virtual link entry name. required: true + type: str peer: description: - A.B.C.D, peer router ID. - retransmit-interval: + type: str + retransmit_interval: description: - Retransmit interval. - transmit-delay: + type: int + transmit_delay: description: - Transmit delay. - auto-cost-ref-bandwidth: + type: int + auto_cost_ref_bandwidth: description: - Reference bandwidth in terms of megabits per second. + type: int bfd: description: - Enable/disable Bidirectional Forwarding Detection (BFD). + type: str choices: - enable - disable - default-information-metric: + default_information_metric: description: - Default information metric. - default-information-metric-type: + type: int + default_information_metric_type: description: - Default information metric type. + type: str choices: - 1 - 2 - default-information-originate: + default_information_originate: description: - Enable/disable generation of default route. + type: str choices: - enable - always - disable - default-information-route-map: + default_information_route_map: description: - Default information route map. Source router.route-map.name. - default-metric: + type: str + default_metric: description: - Default metric of redistribute routes. - log-neighbour-changes: + type: int + log_neighbour_changes: description: - Enable logging of OSPFv3 neighbour's changes + type: str choices: - enable - disable - ospf6-interface: + ospf6_interface: description: - OSPF6 interface configuration. + type: list suboptions: - area-id: + area_id: description: - A.B.C.D, in IPv4 address format. + type: str bfd: description: - Enable/disable Bidirectional Forwarding Detection (BFD). + type: str choices: - global - enable @@ -225,39 +265,61 @@ options: cost: description: - Cost of the interface, value range from 0 to 65535, 0 means auto-cost. - dead-interval: + type: int + dead_interval: description: - Dead interval. - hello-interval: + type: int + hello_interval: description: - Hello interval. + type: int interface: description: - Configuration interface name. Source system.interface.name. + type: str + mtu: + description: + - MTU for OSPFv3 packets. + type: int + mtu_ignore: + description: + - Enable/disable ignoring MTU field in DBD packets. + type: str + choices: + - enable + - disable name: description: - Interface entry name. required: true + type: str neighbor: description: - OSPFv3 neighbors are used when OSPFv3 runs on non-broadcast media + type: list suboptions: cost: description: - Cost of the interface, value range from 0 to 65535, 0 means auto-cost. + type: int ip6: description: - IPv6 link local address of the neighbor. required: true - poll-interval: + type: str + poll_interval: description: - Poll interval time in seconds. + type: int priority: description: - priority - network-type: + type: int + network_type: description: - Network type. + type: str choices: - broadcast - point-to-point @@ -267,36 +329,45 @@ options: priority: description: - priority - retransmit-interval: + type: int + retransmit_interval: description: - Retransmit interval. + type: int status: description: - Enable/disable OSPF6 routing on this interface. + type: str choices: - disable - enable - transmit-delay: + transmit_delay: description: - Transmit delay. - passive-interface: + type: int + passive_interface: description: - Passive interface configuration. + type: list suboptions: name: description: - Passive interface name. Source system.interface.name. required: true + type: str redistribute: description: - Redistribute configuration. + type: list suboptions: metric: description: - Redistribute metric setting. - metric-type: + type: int + metric_type: description: - Metric type. + type: str choices: - 1 - 2 @@ -304,28 +375,35 @@ options: description: - Redistribute name. required: true + type: str routemap: description: - Route map name. Source router.route-map.name. + type: str status: description: - status + type: str choices: - enable - disable - router-id: + router_id: description: - A.B.C.D, in IPv4 address format. - spf-timers: + type: str + spf_timers: description: - SPF calculation frequency. - summary-address: + type: str + summary_address: description: - IPv6 address summary configuration. + type: list suboptions: advertise: description: - Enable/disable advertise status. + type: str choices: - disable - enable @@ -333,12 +411,15 @@ options: description: - Summary address entry ID. required: true + type: int prefix6: description: - IPv6 prefix. + type: str tag: description: - Tag value. + type: int ''' EXAMPLES = ''' @@ -348,6 +429,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPv6 OSPF. fortios_router_ospf6: @@ -357,77 +439,79 @@ EXAMPLES = ''' vdom: "{{ vdom }}" https: "False" router_ospf6: - abr-type: "cisco" + abr_type: "cisco" area: - - default-cost: "5" + default_cost: "5" id: "6" - nssa-default-information-originate: "enable" - nssa-default-information-originate-metric: "8" - nssa-default-information-originate-metric-type: "1" - nssa-redistribution: "enable" - nssa-translator-role: "candidate" + nssa_default_information_originate: "enable" + nssa_default_information_originate_metric: "8" + nssa_default_information_originate_metric_type: "1" + nssa_redistribution: "enable" + nssa_translator_role: "candidate" range: - advertise: "disable" id: "14" prefix6: "" - stub-type: "no-summary" + stub_type: "no-summary" type: "regular" - virtual-link: + virtual_link: - - dead-interval: "19" - hello-interval: "20" + dead_interval: "19" + hello_interval: "20" name: "default_name_21" peer: "" - retransmit-interval: "23" - transmit-delay: "24" - auto-cost-ref-bandwidth: "25" + retransmit_interval: "23" + transmit_delay: "24" + auto_cost_ref_bandwidth: "25" bfd: "enable" - default-information-metric: "27" - default-information-metric-type: "1" - default-information-originate: "enable" - default-information-route-map: " (source router.route-map.name)" - default-metric: "31" - log-neighbour-changes: "enable" - ospf6-interface: + default_information_metric: "27" + default_information_metric_type: "1" + default_information_originate: "enable" + default_information_route_map: " (source router.route-map.name)" + default_metric: "31" + log_neighbour_changes: "enable" + ospf6_interface: - - area-id: "" + area_id: "" bfd: "global" cost: "36" - dead-interval: "37" - hello-interval: "38" + dead_interval: "37" + hello_interval: "38" interface: " (source system.interface.name)" - name: "default_name_40" + mtu: "40" + mtu_ignore: "enable" + name: "default_name_42" neighbor: - - cost: "42" + cost: "44" ip6: "" - poll-interval: "44" - priority: "45" - network-type: "broadcast" - priority: "47" - retransmit-interval: "48" + poll_interval: "46" + priority: "47" + network_type: "broadcast" + priority: "49" + retransmit_interval: "50" status: "disable" - transmit-delay: "50" - passive-interface: + transmit_delay: "52" + passive_interface: - - name: "default_name_52 (source system.interface.name)" + name: "default_name_54 (source system.interface.name)" redistribute: - - metric: "54" - metric-type: "1" - name: "default_name_56" + metric: "56" + metric_type: "1" + name: "default_name_58" routemap: " (source router.route-map.name)" status: "enable" - router-id: "" - spf-timers: "" - summary-address: + router_id: "" + spf_timers: "" + summary_address: - advertise: "disable" - id: "63" + id: "65" prefix6: "" - tag: "65" + tag: "67" ''' RETURN = ''' @@ -490,14 +574,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -505,16 +591,16 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_ospf6_data(json): - option_list = ['abr-type', 'area', 'auto-cost-ref-bandwidth', - 'bfd', 'default-information-metric', 'default-information-metric-type', - 'default-information-originate', 'default-information-route-map', 'default-metric', - 'log-neighbour-changes', 'ospf6-interface', 'passive-interface', - 'redistribute', 'router-id', 'spf-timers', - 'summary-address'] + option_list = ['abr_type', 'area', 'auto_cost_ref_bandwidth', + 'bfd', 'default_information_metric', 'default_information_metric_type', + 'default_information_originate', 'default_information_route_map', 'default_metric', + 'log_neighbour_changes', 'ospf6_interface', 'passive_interface', + 'redistribute', 'router_id', 'spf_timers', + 'summary_address'] dictionary = {} for attribute in option_list: @@ -524,17 +610,15 @@ def filter_router_ospf6_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data @@ -542,48 +626,54 @@ def flatten_multilists_attributes(data): def router_ospf6(data, fos): vdom = data['vdom'] router_ospf6_data = data['router_ospf6'] - flattened_data = flatten_multilists_attributes(router_ospf6_data) - filtered_data = filter_router_ospf6_data(flattened_data) + filtered_data = underscore_to_hyphen(filter_router_ospf6_data(router_ospf6_data)) + return fos.set('router', 'ospf6', data=filtered_data, vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_ospf6']: resp = router_ospf6(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, "router_ospf6": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "abr-type": {"required": False, "type": "str", + "abr_type": {"required": False, "type": "str", "choices": ["cisco", "ibm", "standard"]}, "area": {"required": False, "type": "list", "options": { - "default-cost": {"required": False, "type": "int"}, + "default_cost": {"required": False, "type": "int"}, "id": {"required": True, "type": "str"}, - "nssa-default-information-originate": {"required": False, "type": "str", + "nssa_default_information_originate": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "nssa-default-information-originate-metric": {"required": False, "type": "int"}, - "nssa-default-information-originate-metric-type": {"required": False, "type": "str", + "nssa_default_information_originate_metric": {"required": False, "type": "int"}, + "nssa_default_information_originate_metric_type": {"required": False, "type": "str", "choices": ["1", "2"]}, - "nssa-redistribution": {"required": False, "type": "str", + "nssa_redistribution": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "nssa-translator-role": {"required": False, "type": "str", + "nssa_translator_role": {"required": False, "type": "str", "choices": ["candidate", "never", "always"]}, "range": {"required": False, "type": "list", "options": { @@ -592,75 +682,78 @@ def main(): "id": {"required": True, "type": "int"}, "prefix6": {"required": False, "type": "str"} }}, - "stub-type": {"required": False, "type": "str", + "stub_type": {"required": False, "type": "str", "choices": ["no-summary", "summary"]}, "type": {"required": False, "type": "str", "choices": ["regular", "nssa", "stub"]}, - "virtual-link": {"required": False, "type": "list", + "virtual_link": {"required": False, "type": "list", "options": { - "dead-interval": {"required": False, "type": "int"}, - "hello-interval": {"required": False, "type": "int"}, + "dead_interval": {"required": False, "type": "int"}, + "hello_interval": {"required": False, "type": "int"}, "name": {"required": True, "type": "str"}, "peer": {"required": False, "type": "str"}, - "retransmit-interval": {"required": False, "type": "int"}, - "transmit-delay": {"required": False, "type": "int"} + "retransmit_interval": {"required": False, "type": "int"}, + "transmit_delay": {"required": False, "type": "int"} }} }}, - "auto-cost-ref-bandwidth": {"required": False, "type": "int"}, + "auto_cost_ref_bandwidth": {"required": False, "type": "int"}, "bfd": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "default-information-metric": {"required": False, "type": "int"}, - "default-information-metric-type": {"required": False, "type": "str", + "default_information_metric": {"required": False, "type": "int"}, + "default_information_metric_type": {"required": False, "type": "str", "choices": ["1", "2"]}, - "default-information-originate": {"required": False, "type": "str", + "default_information_originate": {"required": False, "type": "str", "choices": ["enable", "always", "disable"]}, - "default-information-route-map": {"required": False, "type": "str"}, - "default-metric": {"required": False, "type": "int"}, - "log-neighbour-changes": {"required": False, "type": "str", + "default_information_route_map": {"required": False, "type": "str"}, + "default_metric": {"required": False, "type": "int"}, + "log_neighbour_changes": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "ospf6-interface": {"required": False, "type": "list", + "ospf6_interface": {"required": False, "type": "list", "options": { - "area-id": {"required": False, "type": "str"}, + "area_id": {"required": False, "type": "str"}, "bfd": {"required": False, "type": "str", "choices": ["global", "enable", "disable"]}, "cost": {"required": False, "type": "int"}, - "dead-interval": {"required": False, "type": "int"}, - "hello-interval": {"required": False, "type": "int"}, + "dead_interval": {"required": False, "type": "int"}, + "hello_interval": {"required": False, "type": "int"}, "interface": {"required": False, "type": "str"}, + "mtu": {"required": False, "type": "int"}, + "mtu_ignore": {"required": False, "type": "str", + "choices": ["enable", "disable"]}, "name": {"required": True, "type": "str"}, "neighbor": {"required": False, "type": "list", "options": { "cost": {"required": False, "type": "int"}, "ip6": {"required": True, "type": "str"}, - "poll-interval": {"required": False, "type": "int"}, + "poll_interval": {"required": False, "type": "int"}, "priority": {"required": False, "type": "int"} }}, - "network-type": {"required": False, "type": "str", + "network_type": {"required": False, "type": "str", "choices": ["broadcast", "point-to-point", "non-broadcast", "point-to-multipoint", "point-to-multipoint-non-broadcast"]}, "priority": {"required": False, "type": "int"}, - "retransmit-interval": {"required": False, "type": "int"}, + "retransmit_interval": {"required": False, "type": "int"}, "status": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "transmit-delay": {"required": False, "type": "int"} + "transmit_delay": {"required": False, "type": "int"} }}, - "passive-interface": {"required": False, "type": "list", + "passive_interface": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, "redistribute": {"required": False, "type": "list", "options": { "metric": {"required": False, "type": "int"}, - "metric-type": {"required": False, "type": "str", + "metric_type": {"required": False, "type": "str", "choices": ["1", "2"]}, "name": {"required": True, "type": "str"}, "routemap": {"required": False, "type": "str"}, "status": {"required": False, "type": "str", "choices": ["enable", "disable"]} }}, - "router-id": {"required": False, "type": "str"}, - "spf-timers": {"required": False, "type": "str"}, - "summary-address": {"required": False, "type": "list", + "router_id": {"required": False, "type": "str"}, + "spf_timers": {"required": False, "type": "str"}, + "summary_address": {"required": False, "type": "list", "options": { "advertise": {"required": False, "type": "str", "choices": ["disable", "enable"]}, @@ -675,15 +768,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_policy.py b/lib/ansible/modules/network/fortios/fortios_router_policy.py index 640aef3f9e8..65b4e738703 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_policy.py +++ b/lib/ansible/modules/network/fortios/fortios_router_policy.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_policy short_description: Configure IPv4 routing policies in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and policy category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,138 +41,175 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 router_policy: description: - Configure IPv4 routing policies. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent action: description: - Action of the policy route. + type: str choices: - deny - permit comments: description: - Optional comments. + type: str dst: description: - Destination IP and mask (x.x.x.x/x). + type: list suboptions: subnet: description: - IP and mask. required: true - dst-negate: + type: str + dst_negate: description: - Enable/disable negating destination address match. + type: str choices: - enable - disable dstaddr: description: - Destination address name. + type: list suboptions: name: description: - Address/group name. Source firewall.address.name firewall.addrgrp.name. required: true - end-port: + type: str + end_port: description: - End destination port number (0 - 65535). - end-source-port: + type: int + end_source_port: description: - End source port number (0 - 65535). + type: int gateway: description: - IP address of the gateway. - input-device: + type: str + input_device: description: - Incoming interface name. + type: list suboptions: name: description: - Interface name. Source system.interface.name. required: true - output-device: + type: str + output_device: description: - Outgoing interface name. Source system.interface.name. + type: str protocol: description: - Protocol number (0 - 255). - seq-num: + type: int + seq_num: description: - Sequence number. - required: true + type: int src: description: - Source IP and mask (x.x.x.x/x). + type: list suboptions: subnet: description: - IP and mask. required: true - src-negate: + type: str + src_negate: description: - Enable/disable negating source address match. + type: str choices: - enable - disable srcaddr: description: - Source address name. + type: list suboptions: name: description: - Address/group name. Source firewall.address.name firewall.addrgrp.name. required: true - start-port: + type: str + start_port: description: - Start destination port number (0 - 65535). - start-source-port: + type: int + start_source_port: description: - Start source port number (0 - 65535). + type: int status: description: - Enable/disable this policy route. + type: str choices: - enable - disable tos: description: - Type of service bit pattern. - tos-mask: + type: str + tos_mask: description: - Type of service evaluated bits. + type: str ''' EXAMPLES = ''' @@ -185,6 +219,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPv4 routing policies. fortios_router_policy: @@ -193,38 +228,38 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" router_policy: - state: "present" action: "deny" comments: "" dst: - subnet: "" - dst-negate: "enable" + dst_negate: "enable" dstaddr: - name: "default_name_9 (source firewall.address.name firewall.addrgrp.name)" - end-port: "10" - end-source-port: "11" + end_port: "10" + end_source_port: "11" gateway: "" - input-device: + input_device: - name: "default_name_14 (source system.interface.name)" - output-device: " (source system.interface.name)" + output_device: " (source system.interface.name)" protocol: "16" - seq-num: "17" + seq_num: "17" src: - subnet: "" - src-negate: "enable" + src_negate: "enable" srcaddr: - name: "default_name_22 (source firewall.address.name firewall.addrgrp.name)" - start-port: "23" - start-source-port: "24" + start_port: "23" + start_source_port: "24" status: "enable" tos: "" - tos-mask: "" + tos_mask: "" ''' RETURN = ''' @@ -287,14 +322,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -302,17 +339,17 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_policy_data(json): option_list = ['action', 'comments', 'dst', - 'dst-negate', 'dstaddr', 'end-port', - 'end-source-port', 'gateway', 'input-device', - 'output-device', 'protocol', 'seq-num', - 'src', 'src-negate', 'srcaddr', - 'start-port', 'start-source-port', 'status', - 'tos', 'tos-mask'] + 'dst_negate', 'dstaddr', 'end_port', + 'end_source_port', 'gateway', 'input_device', + 'output_device', 'protocol', 'seq_num', + 'src', 'src_negate', 'srcaddr', + 'start_port', 'start_source_port', 'status', + 'tos', 'tos_mask'] dictionary = {} for attribute in option_list: @@ -322,61 +359,66 @@ def filter_router_policy_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data def router_policy(data, fos): vdom = data['vdom'] + state = data['state'] router_policy_data = data['router_policy'] - flattened_data = flatten_multilists_attributes(router_policy_data) - filtered_data = filter_router_policy_data(flattened_data) - if router_policy_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_router_policy_data(router_policy_data)) + + if state == "present": return fos.set('router', 'policy', data=filtered_data, vdom=vdom) - elif router_policy_data['state'] == "absent": + elif state == "absent": return fos.delete('router', 'policy', mkey=filtered_data['seq-num'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_policy']: resp = router_policy(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "router_policy": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "action": {"required": False, "type": "str", "choices": ["deny", "permit"]}, "comments": {"required": False, "type": "str"}, @@ -384,38 +426,38 @@ def main(): "options": { "subnet": {"required": True, "type": "str"} }}, - "dst-negate": {"required": False, "type": "str", + "dst_negate": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "dstaddr": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, - "end-port": {"required": False, "type": "int"}, - "end-source-port": {"required": False, "type": "int"}, + "end_port": {"required": False, "type": "int"}, + "end_source_port": {"required": False, "type": "int"}, "gateway": {"required": False, "type": "str"}, - "input-device": {"required": False, "type": "list", + "input_device": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, - "output-device": {"required": False, "type": "str"}, + "output_device": {"required": False, "type": "str"}, "protocol": {"required": False, "type": "int"}, - "seq-num": {"required": True, "type": "int"}, + "seq_num": {"required": False, "type": "int"}, "src": {"required": False, "type": "list", "options": { "subnet": {"required": True, "type": "str"} }}, - "src-negate": {"required": False, "type": "str", + "src_negate": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "srcaddr": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, - "start-port": {"required": False, "type": "int"}, - "start-source-port": {"required": False, "type": "int"}, + "start_port": {"required": False, "type": "int"}, + "start_source_port": {"required": False, "type": "int"}, "status": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "tos": {"required": False, "type": "str"}, - "tos-mask": {"required": False, "type": "str"} + "tos_mask": {"required": False, "type": "str"} } } @@ -423,15 +465,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_policy6.py b/lib/ansible/modules/network/fortios/fortios_router_policy6.py index 075a31897d9..8b67c7b5994 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_policy6.py +++ b/lib/ansible/modules/network/fortios/fortios_router_policy6.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_policy6 short_description: Configure IPv6 routing policies in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and policy6 category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,83 +41,108 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 router_policy6: description: - Configure IPv6 routing policies. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent comments: description: - Optional comments. + type: str dst: description: - Destination IPv6 prefix. - end-port: + type: str + end_port: description: - End destination port number (1 - 65535). + type: int gateway: description: - IPv6 address of the gateway. - input-device: + type: str + input_device: description: - Incoming interface name. Source system.interface.name. - output-device: + type: str + output_device: description: - Outgoing interface name. Source system.interface.name. + type: str protocol: description: - Protocol number (0 - 255). - seq-num: + type: int + seq_num: description: - Sequence number. - required: true + type: int src: description: - Source IPv6 prefix. - start-port: + type: str + start_port: description: - Start destination port number (1 - 65535). + type: int status: description: - Enable/disable this policy route. + type: str choices: - enable - disable tos: description: - Type of service bit pattern. - tos-mask: + type: str + tos_mask: description: - Type of service evaluated bits. + type: str ''' EXAMPLES = ''' @@ -130,6 +152,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPv6 routing policies. fortios_router_policy6: @@ -138,21 +161,21 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" router_policy6: - state: "present" comments: "" dst: "" - end-port: "5" + end_port: "5" gateway: "" - input-device: " (source system.interface.name)" - output-device: " (source system.interface.name)" + input_device: " (source system.interface.name)" + output_device: " (source system.interface.name)" protocol: "9" - seq-num: "10" + seq_num: "10" src: "" - start-port: "12" + start_port: "12" status: "enable" tos: "" - tos-mask: "" + tos_mask: "" ''' RETURN = ''' @@ -215,14 +238,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -230,15 +255,15 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_policy6_data(json): - option_list = ['comments', 'dst', 'end-port', - 'gateway', 'input-device', 'output-device', - 'protocol', 'seq-num', 'src', - 'start-port', 'status', 'tos', - 'tos-mask'] + option_list = ['comments', 'dst', 'end_port', + 'gateway', 'input_device', 'output_device', + 'protocol', 'seq_num', 'src', + 'start_port', 'status', 'tos', + 'tos_mask'] dictionary = {} for attribute in option_list: @@ -248,75 +273,80 @@ def filter_router_policy6_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data def router_policy6(data, fos): vdom = data['vdom'] + state = data['state'] router_policy6_data = data['router_policy6'] - flattened_data = flatten_multilists_attributes(router_policy6_data) - filtered_data = filter_router_policy6_data(flattened_data) - if router_policy6_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_router_policy6_data(router_policy6_data)) + + if state == "present": return fos.set('router', 'policy6', data=filtered_data, vdom=vdom) - elif router_policy6_data['state'] == "absent": + elif state == "absent": return fos.delete('router', 'policy6', mkey=filtered_data['seq-num'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_policy6']: resp = router_policy6(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "router_policy6": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "comments": {"required": False, "type": "str"}, "dst": {"required": False, "type": "str"}, - "end-port": {"required": False, "type": "int"}, + "end_port": {"required": False, "type": "int"}, "gateway": {"required": False, "type": "str"}, - "input-device": {"required": False, "type": "str"}, - "output-device": {"required": False, "type": "str"}, + "input_device": {"required": False, "type": "str"}, + "output_device": {"required": False, "type": "str"}, "protocol": {"required": False, "type": "int"}, - "seq-num": {"required": True, "type": "int"}, + "seq_num": {"required": False, "type": "int"}, "src": {"required": False, "type": "str"}, - "start-port": {"required": False, "type": "int"}, + "start_port": {"required": False, "type": "int"}, "status": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "tos": {"required": False, "type": "str"}, - "tos-mask": {"required": False, "type": "str"} + "tos_mask": {"required": False, "type": "str"} } } @@ -324,15 +354,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_prefix_list.py b/lib/ansible/modules/network/fortios/fortios_router_prefix_list.py index 921b82565e0..126ad7163c8 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_prefix_list.py +++ b/lib/ansible/modules/network/fortios/fortios_router_prefix_list.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_prefix_list short_description: Configure IPv4 prefix lists in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and prefix_list category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,73 +41,95 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 router_prefix_list: description: - Configure IPv4 prefix lists. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent comments: description: - Comment. + type: str name: description: - Name. required: true + type: str rule: description: - IPv4 prefix list rule. + type: list suboptions: action: description: - Permit or deny this IP address and netmask prefix. + type: str choices: - permit - deny flags: description: - Flags. + type: int ge: description: - Minimum prefix length to be matched (0 - 32). + type: int id: description: - Rule ID. required: true + type: int le: description: - Maximum prefix length to be matched (0 - 32). + type: int prefix: description: - IPv4 prefix to define regular filter criteria, such as "any" or subnets. + type: str ''' EXAMPLES = ''' @@ -120,6 +139,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPv4 prefix lists. fortios_router_prefix_list: @@ -128,8 +148,8 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" router_prefix_list: - state: "present" comments: "" name: "default_name_4" rule: @@ -202,14 +222,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -217,7 +239,7 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_prefix_list_data(json): @@ -231,61 +253,66 @@ def filter_router_prefix_list_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data def router_prefix_list(data, fos): vdom = data['vdom'] + state = data['state'] router_prefix_list_data = data['router_prefix_list'] - flattened_data = flatten_multilists_attributes(router_prefix_list_data) - filtered_data = filter_router_prefix_list_data(flattened_data) - if router_prefix_list_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_router_prefix_list_data(router_prefix_list_data)) + + if state == "present": return fos.set('router', 'prefix-list', data=filtered_data, vdom=vdom) - elif router_prefix_list_data['state'] == "absent": + elif state == "absent": return fos.delete('router', 'prefix-list', mkey=filtered_data['name'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_prefix_list']: resp = router_prefix_list(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "router_prefix_list": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "comments": {"required": False, "type": "str"}, "name": {"required": True, "type": "str"}, "rule": {"required": False, "type": "list", @@ -305,15 +332,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_rip.py b/lib/ansible/modules/network/fortios/fortios_router_rip.py index 3d372c24f25..89d6376c31b 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_rip.py +++ b/lib/ansible/modules/network/fortios/fortios_router_rip.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_rip short_description: Configure RIP in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and rip category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,67 +41,86 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 router_rip: description: - Configure RIP. default: null + type: dict suboptions: - default-information-originate: + default_information_originate: description: - Enable/disable generation of default route. + type: str choices: - enable - disable - default-metric: + default_metric: description: - Default metric. + type: int distance: description: - distance + type: list suboptions: - access-list: + access_list: description: - Access list for route destination. Source router.access-list.name. + type: str distance: description: - Distance (1 - 255). + type: int id: description: - Distance ID. required: true + type: int prefix: description: - Distance prefix. - distribute-list: + type: str + distribute_list: description: - Distribute list. + type: list suboptions: direction: description: - Distribute list direction. + type: str choices: - in - out @@ -112,110 +128,136 @@ options: description: - Distribute list ID. required: true + type: int interface: description: - Distribute list interface name. Source system.interface.name. + type: str listname: description: - Distribute access/prefix list name. Source router.access-list.name router.prefix-list.name. + type: str status: description: - status + type: str choices: - enable - disable - garbage-timer: + garbage_timer: description: - Garbage timer in seconds. + type: int interface: description: - RIP interface configuration. + type: list suboptions: - auth-keychain: + auth_keychain: description: - Authentication key-chain name. Source router.key-chain.name. - auth-mode: + type: str + auth_mode: description: - Authentication mode. + type: str choices: - none - text - md5 - auth-string: + auth_string: description: - Authentication string/password. + type: str flags: description: - flags + type: int name: description: - Interface name. Source system.interface.name. required: true - receive-version: + type: str + receive_version: description: - Receive version. + type: str choices: - 1 - 2 - send-version: + send_version: description: - Send version. + type: str choices: - 1 - 2 - send-version2-broadcast: + send_version2_broadcast: description: - Enable/disable broadcast version 1 compatible packets. + type: str choices: - disable - enable - split-horizon: + split_horizon: description: - Enable/disable split horizon. + type: str choices: - poisoned - regular - split-horizon-status: + split_horizon_status: description: - Enable/disable split horizon. + type: str choices: - enable - disable - max-out-metric: + max_out_metric: description: - Maximum metric allowed to output(0 means 'not set'). + type: int neighbor: description: - neighbor + type: list suboptions: id: description: - Neighbor entry ID. required: true + type: int ip: description: - IP address. + type: str network: description: - network + type: list suboptions: id: description: - Network entry ID. required: true + type: int prefix: description: - Network prefix. - offset-list: + type: str + offset_list: description: - Offset list. + type: list suboptions: - access-list: + access_list: description: - Access list name. Source router.access-list.name. + type: str direction: description: - Offset list direction. + type: str choices: - in - out @@ -223,58 +265,73 @@ options: description: - Offset-list ID. required: true + type: int interface: description: - Interface name. Source system.interface.name. + type: str offset: description: - offset + type: int status: description: - status + type: str choices: - enable - disable - passive-interface: + passive_interface: description: - Passive interface configuration. + type: list suboptions: name: description: - Passive interface name. Source system.interface.name. required: true - recv-buffer-size: + type: str + recv_buffer_size: description: - Receiving buffer size. + type: int redistribute: description: - Redistribute configuration. + type: list suboptions: metric: description: - Redistribute metric setting. + type: int name: description: - Redistribute name. required: true + type: str routemap: description: - Route map name. Source router.route-map.name. + type: str status: description: - status + type: str choices: - enable - disable - timeout-timer: + timeout_timer: description: - Timeout timer in seconds. - update-timer: + type: int + update_timer: description: - Update timer in seconds. + type: int version: description: - RIP version. + type: str choices: - 1 - 2 @@ -287,6 +344,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure RIP. fortios_router_rip: @@ -296,35 +354,35 @@ EXAMPLES = ''' vdom: "{{ vdom }}" https: "False" router_rip: - default-information-originate: "enable" - default-metric: "4" + default_information_originate: "enable" + default_metric: "4" distance: - - access-list: " (source router.access-list.name)" + access_list: " (source router.access-list.name)" distance: "7" id: "8" prefix: "" - distribute-list: + distribute_list: - direction: "in" id: "12" interface: " (source system.interface.name)" listname: " (source router.access-list.name router.prefix-list.name)" status: "enable" - garbage-timer: "16" + garbage_timer: "16" interface: - - auth-keychain: " (source router.key-chain.name)" - auth-mode: "none" - auth-string: "" + auth_keychain: " (source router.key-chain.name)" + auth_mode: "none" + auth_string: "" flags: "21" name: "default_name_22 (source system.interface.name)" - receive-version: "1" - send-version: "1" - send-version2-broadcast: "disable" - split-horizon: "poisoned" - split-horizon-status: "enable" - max-out-metric: "28" + receive_version: "1" + send_version: "1" + send_version2_broadcast: "disable" + split_horizon: "poisoned" + split_horizon_status: "enable" + max_out_metric: "28" neighbor: - id: "30" @@ -333,26 +391,26 @@ EXAMPLES = ''' - id: "33" prefix: "" - offset-list: + offset_list: - - access-list: " (source router.access-list.name)" + access_list: " (source router.access-list.name)" direction: "in" id: "38" interface: " (source system.interface.name)" offset: "40" status: "enable" - passive-interface: + passive_interface: - name: "default_name_43 (source system.interface.name)" - recv-buffer-size: "44" + recv_buffer_size: "44" redistribute: - metric: "46" name: "default_name_47" routemap: " (source router.route-map.name)" status: "enable" - timeout-timer: "50" - update-timer: "51" + timeout_timer: "50" + update_timer: "51" version: "1" ''' @@ -416,14 +474,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -431,15 +491,15 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_rip_data(json): - option_list = ['default-information-originate', 'default-metric', 'distance', - 'distribute-list', 'garbage-timer', 'interface', - 'max-out-metric', 'neighbor', 'network', - 'offset-list', 'passive-interface', 'recv-buffer-size', - 'redistribute', 'timeout-timer', 'update-timer', + option_list = ['default_information_originate', 'default_metric', 'distance', + 'distribute_list', 'garbage_timer', 'interface', + 'max_out_metric', 'neighbor', 'network', + 'offset_list', 'passive_interface', 'recv_buffer_size', + 'redistribute', 'timeout_timer', 'update_timer', 'version'] dictionary = {} @@ -450,17 +510,15 @@ def filter_router_rip_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data @@ -468,45 +526,51 @@ def flatten_multilists_attributes(data): def router_rip(data, fos): vdom = data['vdom'] router_rip_data = data['router_rip'] - flattened_data = flatten_multilists_attributes(router_rip_data) - filtered_data = filter_router_rip_data(flattened_data) + filtered_data = underscore_to_hyphen(filter_router_rip_data(router_rip_data)) + return fos.set('router', 'rip', data=filtered_data, vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_rip']: resp = router_rip(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, "router_rip": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "default-information-originate": {"required": False, "type": "str", + "default_information_originate": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "default-metric": {"required": False, "type": "int"}, + "default_metric": {"required": False, "type": "int"}, "distance": {"required": False, "type": "list", "options": { - "access-list": {"required": False, "type": "str"}, + "access_list": {"required": False, "type": "str"}, "distance": {"required": False, "type": "int"}, "id": {"required": True, "type": "int"}, "prefix": {"required": False, "type": "str"} }}, - "distribute-list": {"required": False, "type": "list", + "distribute_list": {"required": False, "type": "list", "options": { "direction": {"required": False, "type": "str", "choices": ["in", "out"]}, @@ -516,27 +580,27 @@ def main(): "status": {"required": False, "type": "str", "choices": ["enable", "disable"]} }}, - "garbage-timer": {"required": False, "type": "int"}, + "garbage_timer": {"required": False, "type": "int"}, "interface": {"required": False, "type": "list", "options": { - "auth-keychain": {"required": False, "type": "str"}, - "auth-mode": {"required": False, "type": "str", + "auth_keychain": {"required": False, "type": "str"}, + "auth_mode": {"required": False, "type": "str", "choices": ["none", "text", "md5"]}, - "auth-string": {"required": False, "type": "str"}, + "auth_string": {"required": False, "type": "str"}, "flags": {"required": False, "type": "int"}, "name": {"required": True, "type": "str"}, - "receive-version": {"required": False, "type": "str", + "receive_version": {"required": False, "type": "str", "choices": ["1", "2"]}, - "send-version": {"required": False, "type": "str", + "send_version": {"required": False, "type": "str", "choices": ["1", "2"]}, - "send-version2-broadcast": {"required": False, "type": "str", + "send_version2_broadcast": {"required": False, "type": "str", "choices": ["disable", "enable"]}, - "split-horizon": {"required": False, "type": "str", + "split_horizon": {"required": False, "type": "str", "choices": ["poisoned", "regular"]}, - "split-horizon-status": {"required": False, "type": "str", + "split_horizon_status": {"required": False, "type": "str", "choices": ["enable", "disable"]} }}, - "max-out-metric": {"required": False, "type": "int"}, + "max_out_metric": {"required": False, "type": "int"}, "neighbor": {"required": False, "type": "list", "options": { "id": {"required": True, "type": "int"}, @@ -547,9 +611,9 @@ def main(): "id": {"required": True, "type": "int"}, "prefix": {"required": False, "type": "str"} }}, - "offset-list": {"required": False, "type": "list", + "offset_list": {"required": False, "type": "list", "options": { - "access-list": {"required": False, "type": "str"}, + "access_list": {"required": False, "type": "str"}, "direction": {"required": False, "type": "str", "choices": ["in", "out"]}, "id": {"required": True, "type": "int"}, @@ -558,11 +622,11 @@ def main(): "status": {"required": False, "type": "str", "choices": ["enable", "disable"]} }}, - "passive-interface": {"required": False, "type": "list", + "passive_interface": {"required": False, "type": "list", "options": { "name": {"required": True, "type": "str"} }}, - "recv-buffer-size": {"required": False, "type": "int"}, + "recv_buffer_size": {"required": False, "type": "int"}, "redistribute": {"required": False, "type": "list", "options": { "metric": {"required": False, "type": "int"}, @@ -571,8 +635,8 @@ def main(): "status": {"required": False, "type": "str", "choices": ["enable", "disable"]} }}, - "timeout-timer": {"required": False, "type": "int"}, - "update-timer": {"required": False, "type": "int"}, + "timeout_timer": {"required": False, "type": "int"}, + "update_timer": {"required": False, "type": "int"}, "version": {"required": False, "type": "str", "choices": ["1", "2"]} @@ -582,15 +646,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_setting.py b/lib/ansible/modules/network/fortios/fortios_router_setting.py index f703b03b422..41d39e348ca 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_setting.py +++ b/lib/ansible/modules/network/fortios/fortios_router_setting.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_setting short_description: Configure router settings in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and setting category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,40 +41,52 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 router_setting: description: - Configure router settings. default: null + type: dict suboptions: hostname: description: - Hostname for this virtual domain router. - show-filter: + type: str + show_filter: description: - Prefix-list as filter for showing routes. Source router.prefix-list.name. + type: str ''' EXAMPLES = ''' @@ -87,6 +96,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure router settings. fortios_router_setting: @@ -97,7 +107,7 @@ EXAMPLES = ''' https: "False" router_setting: hostname: "myhostname" - show-filter: " (source router.prefix-list.name)" + show_filter: " (source router.prefix-list.name)" ''' RETURN = ''' @@ -160,14 +170,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -175,11 +187,11 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_setting_data(json): - option_list = ['hostname', 'show-filter'] + option_list = ['hostname', 'show_filter'] dictionary = {} for attribute in option_list: @@ -189,17 +201,15 @@ def filter_router_setting_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data @@ -207,36 +217,42 @@ def flatten_multilists_attributes(data): def router_setting(data, fos): vdom = data['vdom'] router_setting_data = data['router_setting'] - flattened_data = flatten_multilists_attributes(router_setting_data) - filtered_data = filter_router_setting_data(flattened_data) + filtered_data = underscore_to_hyphen(filter_router_setting_data(router_setting_data)) + return fos.set('router', 'setting', data=filtered_data, vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_setting']: resp = router_setting(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, "router_setting": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { "hostname": {"required": False, "type": "str"}, - "show-filter": {"required": False, "type": "str"} + "show_filter": {"required": False, "type": "str"} } } @@ -244,15 +260,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/lib/ansible/modules/network/fortios/fortios_router_static.py b/lib/ansible/modules/network/fortios/fortios_router_static.py index 683c5f466f3..c36d3a51db0 100644 --- a/lib/ansible/modules/network/fortios/fortios_router_static.py +++ b/lib/ansible/modules/network/fortios/fortios_router_static.py @@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function) # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# the lib use python logging can get it if the following is set in your -# Ansible config. __metaclass__ = type @@ -29,10 +26,10 @@ DOCUMENTATION = ''' module: fortios_router_static short_description: Configure IPv4 static routing tables in Fortinet's FortiOS and FortiGate. description: - - This module is able to configure a FortiGate or FortiOS by allowing the + - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the user to set and modify router feature and static category. Examples include all parameters and values need to be adjusted to datasources before usage. - Tested with FOS v6.0.2 + Tested with FOS v6.0.5 version_added: "2.8" author: - Miguel Angel Munoz (@mamunozgonzalez) @@ -44,116 +41,147 @@ requirements: - fortiosapi>=0.9.8 options: host: - description: - - FortiOS or FortiGate ip address. - required: true + description: + - FortiOS or FortiGate IP address. + type: str + required: false username: description: - FortiOS or FortiGate username. - required: true + type: str + required: false password: description: - FortiOS or FortiGate password. + type: str default: "" vdom: description: - Virtual domain, among those defined previously. A vdom is a virtual instance of the FortiGate that can be configured and used as a different unit. + type: str default: root https: description: - - Indicates if the requests towards FortiGate must use HTTPS - protocol + - Indicates if the requests towards FortiGate must use HTTPS protocol. + type: bool + default: true + ssl_verify: + description: + - Ensures FortiGate certificate must be verified by a proper CA. type: bool default: true + version_added: 2.9 + state: + description: + - Indicates whether to create or remove the object. + type: str + required: true + choices: + - present + - absent + version_added: 2.9 router_static: description: - Configure IPv4 static routing tables. default: null + type: dict suboptions: - state: - description: - - Indicates whether to create or remove the object - choices: - - present - - absent bfd: description: - Enable/disable Bidirectional Forwarding Detection (BFD). + type: str choices: - enable - disable blackhole: description: - Enable/disable black hole. + type: str choices: - enable - disable comment: description: - Optional comments. + type: str device: description: - Gateway out interface or tunnel. Source system.interface.name. + type: str distance: description: - Administrative distance (1 - 255). + type: int dst: description: - Destination IP and mask for this route. + type: str dstaddr: description: - Name of firewall address or address group. Source firewall.address.name firewall.addrgrp.name. - dynamic-gateway: + type: str + dynamic_gateway: description: - Enable use of dynamic gateway retrieved from a DHCP or PPP server. + type: str choices: - enable - disable gateway: description: - Gateway IP for this route. - internet-service: + type: str + internet_service: description: - Application ID in the Internet service database. Source firewall.internet-service.id. - internet-service-custom: + type: int + internet_service_custom: description: - Application name in the Internet service custom database. Source firewall.internet-service-custom.name. - link-monitor-exempt: + type: str + link_monitor_exempt: description: - Enable/disable withdrawing this route when link monitor or health check is down. + type: str choices: - enable - disable priority: description: - Administrative priority (0 - 4294967295). - seq-num: + type: int + seq_num: description: - Sequence number. - required: true + type: int src: description: - Source prefix for this route. + type: str status: description: - Enable/disable this static route. + type: str choices: - enable - disable - virtual-wan-link: + virtual_wan_link: description: - Enable/disable egress through the virtual-wan-link. + type: str choices: - enable - disable vrf: description: - Virtual Routing Forwarding ID. + type: int weight: description: - Administrative weight (0 - 255). + type: int ''' EXAMPLES = ''' @@ -163,6 +191,7 @@ EXAMPLES = ''' username: "admin" password: "" vdom: "root" + ssl_verify: "False" tasks: - name: Configure IPv4 static routing tables. fortios_router_static: @@ -171,8 +200,8 @@ EXAMPLES = ''' password: "{{ password }}" vdom: "{{ vdom }}" https: "False" + state: "present" router_static: - state: "present" bfd: "enable" blackhole: "enable" comment: "Optional comments." @@ -180,16 +209,16 @@ EXAMPLES = ''' distance: "7" dst: "" dstaddr: " (source firewall.address.name firewall.addrgrp.name)" - dynamic-gateway: "enable" + dynamic_gateway: "enable" gateway: "" - internet-service: "12 (source firewall.internet-service.id)" - internet-service-custom: " (source firewall.internet-service-custom.name)" - link-monitor-exempt: "enable" + internet_service: "12 (source firewall.internet-service.id)" + internet_service_custom: " (source firewall.internet-service-custom.name)" + link_monitor_exempt: "enable" priority: "15" - seq-num: "16" + seq_num: "16" src: "" status: "enable" - virtual-wan-link: "enable" + virtual_wan_link: "enable" vrf: "20" weight: "21" ''' @@ -254,14 +283,16 @@ version: ''' from ansible.module_utils.basic import AnsibleModule - -fos = None +from ansible.module_utils.connection import Connection +from ansible.module_utils.network.fortios.fortios import FortiOSHandler +from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG -def login(data): +def login(data, fos): host = data['host'] username = data['username'] password = data['password'] + ssl_verify = data['ssl_verify'] fos.debug('on') if 'https' in data and not data['https']: @@ -269,16 +300,16 @@ def login(data): else: fos.https('on') - fos.login(host, username, password) + fos.login(host, username, password, verify=ssl_verify) def filter_router_static_data(json): option_list = ['bfd', 'blackhole', 'comment', 'device', 'distance', 'dst', - 'dstaddr', 'dynamic-gateway', 'gateway', - 'internet-service', 'internet-service-custom', 'link-monitor-exempt', - 'priority', 'seq-num', 'src', - 'status', 'virtual-wan-link', 'vrf', + 'dstaddr', 'dynamic_gateway', 'gateway', + 'internet_service', 'internet_service_custom', 'link_monitor_exempt', + 'priority', 'seq_num', 'src', + 'status', 'virtual_wan_link', 'vrf', 'weight'] dictionary = {} @@ -289,61 +320,66 @@ def filter_router_static_data(json): return dictionary -def flatten_multilists_attributes(data): - multilist_attrs = [] - - for attr in multilist_attrs: - try: - path = "data['" + "']['".join(elem for elem in attr) + "']" - current_val = eval(path) - flattened_val = ' '.join(elem for elem in current_val) - exec(path + '= flattened_val') - except BaseException: - pass +def underscore_to_hyphen(data): + if isinstance(data, list): + for elem in data: + elem = underscore_to_hyphen(elem) + elif isinstance(data, dict): + new_data = {} + for k, v in data.items(): + new_data[k.replace('_', '-')] = underscore_to_hyphen(v) + data = new_data return data def router_static(data, fos): vdom = data['vdom'] + state = data['state'] router_static_data = data['router_static'] - flattened_data = flatten_multilists_attributes(router_static_data) - filtered_data = filter_router_static_data(flattened_data) - if router_static_data['state'] == "present": + filtered_data = underscore_to_hyphen(filter_router_static_data(router_static_data)) + + if state == "present": return fos.set('router', 'static', data=filtered_data, vdom=vdom) - elif router_static_data['state'] == "absent": + elif state == "absent": return fos.delete('router', 'static', mkey=filtered_data['seq-num'], vdom=vdom) +def is_successful_status(status): + return status['status'] == "success" or \ + status['http_method'] == "DELETE" and status['http_status'] == 404 + + def fortios_router(data, fos): - login(data) if data['router_static']: resp = router_static(data, fos) - fos.logout() - return not resp['status'] == "success", resp['status'] == "success", resp + return not is_successful_status(resp), \ + resp['status'] == "success", \ + resp def main(): fields = { - "host": {"required": True, "type": "str"}, - "username": {"required": True, "type": "str"}, - "password": {"required": False, "type": "str", "no_log": True}, + "host": {"required": False, "type": "str"}, + "username": {"required": False, "type": "str"}, + "password": {"required": False, "type": "str", "default": "", "no_log": True}, "vdom": {"required": False, "type": "str", "default": "root"}, "https": {"required": False, "type": "bool", "default": True}, + "ssl_verify": {"required": False, "type": "bool", "default": True}, + "state": {"required": True, "type": "str", + "choices": ["present", "absent"]}, "router_static": { - "required": False, "type": "dict", + "required": False, "type": "dict", "default": None, "options": { - "state": {"required": True, "type": "str", - "choices": ["present", "absent"]}, "bfd": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "blackhole": {"required": False, "type": "str", @@ -353,19 +389,19 @@ def main(): "distance": {"required": False, "type": "int"}, "dst": {"required": False, "type": "str"}, "dstaddr": {"required": False, "type": "str"}, - "dynamic-gateway": {"required": False, "type": "str", + "dynamic_gateway": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "gateway": {"required": False, "type": "str"}, - "internet-service": {"required": False, "type": "int"}, - "internet-service-custom": {"required": False, "type": "str"}, - "link-monitor-exempt": {"required": False, "type": "str", + "internet_service": {"required": False, "type": "int"}, + "internet_service_custom": {"required": False, "type": "str"}, + "link_monitor_exempt": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "priority": {"required": False, "type": "int"}, - "seq-num": {"required": True, "type": "int"}, + "seq_num": {"required": False, "type": "int"}, "src": {"required": False, "type": "str"}, "status": {"required": False, "type": "str", "choices": ["enable", "disable"]}, - "virtual-wan-link": {"required": False, "type": "str", + "virtual_wan_link": {"required": False, "type": "str", "choices": ["enable", "disable"]}, "vrf": {"required": False, "type": "int"}, "weight": {"required": False, "type": "int"} @@ -376,15 +412,31 @@ def main(): module = AnsibleModule(argument_spec=fields, supports_check_mode=False) - try: - from fortiosapi import FortiOSAPI - except ImportError: - module.fail_json(msg="fortiosapi module is required") - global fos - fos = FortiOSAPI() + # legacy_mode refers to using fortiosapi instead of HTTPAPI + legacy_mode = 'host' in module.params and module.params['host'] is not None and \ + 'username' in module.params and module.params['username'] is not None and \ + 'password' in module.params and module.params['password'] is not None + + if not legacy_mode: + if module._socket_path: + connection = Connection(module._socket_path) + fos = FortiOSHandler(connection) + + is_error, has_changed, result = fortios_router(module.params, fos) + else: + module.fail_json(**FAIL_SOCKET_MSG) + else: + try: + from fortiosapi import FortiOSAPI + except ImportError: + module.fail_json(msg="fortiosapi module is required") + + fos = FortiOSAPI() - is_error, has_changed, result = fortios_router(module.params, fos) + login(module.params, fos) + is_error, has_changed, result = fortios_router(module.params, fos) + fos.logout() if not is_error: module.exit_json(changed=has_changed, meta=result) diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index b5441c0d495..97018c855e0 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -3695,43 +3695,6 @@ lib/ansible/modules/network/fortios/fortios_firewall_sniffer.py validate-modules lib/ansible/modules/network/fortios/fortios_ipv4_policy.py validate-modules:E337 lib/ansible/modules/network/fortios/fortios_ipv4_policy.py validate-modules:E338 lib/ansible/modules/network/fortios/fortios_report_chart.py validate-modules:E326 -lib/ansible/modules/network/fortios/fortios_report_layout.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_report_layout.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_report_setting.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_report_setting.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_report_style.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_report_style.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_report_theme.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_report_theme.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_access_list.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_router_access_list.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_auth_path.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_bfd.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_bfd6.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_router_bfd6.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_bgp.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_router_bgp.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_multicast.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_router_multicast.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_multicast6.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_router_multicast6.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_multicast_flow.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_router_multicast_flow.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_ospf.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_router_ospf.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_ospf6.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_router_ospf6.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_policy.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_router_policy.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_policy6.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_router_policy6.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_prefix_list.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_rip.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_router_rip.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_setting.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_router_setting.py validate-modules:E337 -lib/ansible/modules/network/fortios/fortios_router_static.py validate-modules:E336 -lib/ansible/modules/network/fortios/fortios_router_static.py validate-modules:E337 lib/ansible/modules/network/fortios/fortios_spamfilter_profile.py validate-modules:E336 lib/ansible/modules/network/fortios/fortios_spamfilter_profile.py validate-modules:E337 lib/ansible/modules/network/fortios/fortios_ssh_filter_profile.py validate-modules:E336 diff --git a/test/units/modules/network/fortios/test_fortios_report_layout.py b/test/units/modules/network/fortios/test_fortios_report_layout.py new file mode 100644 index 00000000000..63ed503ed52 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_report_layout.py @@ -0,0 +1,329 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_report_layout +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_report_layout.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_report_layout_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_layout': {'cutoff_option': 'run-time', + 'cutoff_time': 'test_value_4', + 'day': 'sunday', + 'description': 'test_value_6', + 'email_recipients': 'test_value_7', + 'email_send': 'enable', + 'format': 'pdf', + 'max_pdf_report': '10', + 'name': 'default_name_11', + 'options': 'include-table-of-content', + 'schedule_type': 'demand', + 'style_theme': 'test_value_14', + 'subtitle': 'test_value_15', + 'time': 'test_value_16', + 'title': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_layout.fortios_report(input_data, fos_instance) + + expected_data = {'cutoff-option': 'run-time', + 'cutoff-time': 'test_value_4', + 'day': 'sunday', + 'description': 'test_value_6', + 'email-recipients': 'test_value_7', + 'email-send': 'enable', + 'format': 'pdf', + 'max-pdf-report': '10', + 'name': 'default_name_11', + 'options': 'include-table-of-content', + 'schedule-type': 'demand', + 'style-theme': 'test_value_14', + 'subtitle': 'test_value_15', + 'time': 'test_value_16', + 'title': 'test_value_17' + } + + set_method_mock.assert_called_with('report', 'layout', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_report_layout_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_layout': {'cutoff_option': 'run-time', + 'cutoff_time': 'test_value_4', + 'day': 'sunday', + 'description': 'test_value_6', + 'email_recipients': 'test_value_7', + 'email_send': 'enable', + 'format': 'pdf', + 'max_pdf_report': '10', + 'name': 'default_name_11', + 'options': 'include-table-of-content', + 'schedule_type': 'demand', + 'style_theme': 'test_value_14', + 'subtitle': 'test_value_15', + 'time': 'test_value_16', + 'title': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_layout.fortios_report(input_data, fos_instance) + + expected_data = {'cutoff-option': 'run-time', + 'cutoff-time': 'test_value_4', + 'day': 'sunday', + 'description': 'test_value_6', + 'email-recipients': 'test_value_7', + 'email-send': 'enable', + 'format': 'pdf', + 'max-pdf-report': '10', + 'name': 'default_name_11', + 'options': 'include-table-of-content', + 'schedule-type': 'demand', + 'style-theme': 'test_value_14', + 'subtitle': 'test_value_15', + 'time': 'test_value_16', + 'title': 'test_value_17' + } + + set_method_mock.assert_called_with('report', 'layout', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_report_layout_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'report_layout': {'cutoff_option': 'run-time', + 'cutoff_time': 'test_value_4', + 'day': 'sunday', + 'description': 'test_value_6', + 'email_recipients': 'test_value_7', + 'email_send': 'enable', + 'format': 'pdf', + 'max_pdf_report': '10', + 'name': 'default_name_11', + 'options': 'include-table-of-content', + 'schedule_type': 'demand', + 'style_theme': 'test_value_14', + 'subtitle': 'test_value_15', + 'time': 'test_value_16', + 'title': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_layout.fortios_report(input_data, fos_instance) + + delete_method_mock.assert_called_with('report', 'layout', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_report_layout_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'report_layout': {'cutoff_option': 'run-time', + 'cutoff_time': 'test_value_4', + 'day': 'sunday', + 'description': 'test_value_6', + 'email_recipients': 'test_value_7', + 'email_send': 'enable', + 'format': 'pdf', + 'max_pdf_report': '10', + 'name': 'default_name_11', + 'options': 'include-table-of-content', + 'schedule_type': 'demand', + 'style_theme': 'test_value_14', + 'subtitle': 'test_value_15', + 'time': 'test_value_16', + 'title': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_layout.fortios_report(input_data, fos_instance) + + delete_method_mock.assert_called_with('report', 'layout', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_report_layout_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_layout': {'cutoff_option': 'run-time', + 'cutoff_time': 'test_value_4', + 'day': 'sunday', + 'description': 'test_value_6', + 'email_recipients': 'test_value_7', + 'email_send': 'enable', + 'format': 'pdf', + 'max_pdf_report': '10', + 'name': 'default_name_11', + 'options': 'include-table-of-content', + 'schedule_type': 'demand', + 'style_theme': 'test_value_14', + 'subtitle': 'test_value_15', + 'time': 'test_value_16', + 'title': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_layout.fortios_report(input_data, fos_instance) + + expected_data = {'cutoff-option': 'run-time', + 'cutoff-time': 'test_value_4', + 'day': 'sunday', + 'description': 'test_value_6', + 'email-recipients': 'test_value_7', + 'email-send': 'enable', + 'format': 'pdf', + 'max-pdf-report': '10', + 'name': 'default_name_11', + 'options': 'include-table-of-content', + 'schedule-type': 'demand', + 'style-theme': 'test_value_14', + 'subtitle': 'test_value_15', + 'time': 'test_value_16', + 'title': 'test_value_17' + } + + set_method_mock.assert_called_with('report', 'layout', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_report_layout_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_layout': { + 'random_attribute_not_valid': 'tag', 'cutoff_option': 'run-time', + 'cutoff_time': 'test_value_4', + 'day': 'sunday', + 'description': 'test_value_6', + 'email_recipients': 'test_value_7', + 'email_send': 'enable', + 'format': 'pdf', + 'max_pdf_report': '10', + 'name': 'default_name_11', + 'options': 'include-table-of-content', + 'schedule_type': 'demand', + 'style_theme': 'test_value_14', + 'subtitle': 'test_value_15', + 'time': 'test_value_16', + 'title': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_layout.fortios_report(input_data, fos_instance) + + expected_data = {'cutoff-option': 'run-time', + 'cutoff-time': 'test_value_4', + 'day': 'sunday', + 'description': 'test_value_6', + 'email-recipients': 'test_value_7', + 'email-send': 'enable', + 'format': 'pdf', + 'max-pdf-report': '10', + 'name': 'default_name_11', + 'options': 'include-table-of-content', + 'schedule-type': 'demand', + 'style-theme': 'test_value_14', + 'subtitle': 'test_value_15', + 'time': 'test_value_16', + 'title': 'test_value_17' + } + + set_method_mock.assert_called_with('report', 'layout', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_report_setting.py b/test/units/modules/network/fortios/test_fortios_report_setting.py new file mode 100644 index 00000000000..b8450dabd74 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_report_setting.py @@ -0,0 +1,183 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_report_setting +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_report_setting.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_report_setting_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_setting': { + 'fortiview': 'enable', + 'pdf_report': 'enable', + 'report_source': 'forward-traffic', + 'top_n': '6', + 'web_browsing_threshold': '7' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_setting.fortios_report(input_data, fos_instance) + + expected_data = { + 'fortiview': 'enable', + 'pdf-report': 'enable', + 'report-source': 'forward-traffic', + 'top-n': '6', + 'web-browsing-threshold': '7' + } + + set_method_mock.assert_called_with('report', 'setting', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_report_setting_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_setting': { + 'fortiview': 'enable', + 'pdf_report': 'enable', + 'report_source': 'forward-traffic', + 'top_n': '6', + 'web_browsing_threshold': '7' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_setting.fortios_report(input_data, fos_instance) + + expected_data = { + 'fortiview': 'enable', + 'pdf-report': 'enable', + 'report-source': 'forward-traffic', + 'top-n': '6', + 'web-browsing-threshold': '7' + } + + set_method_mock.assert_called_with('report', 'setting', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_report_setting_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_setting': { + 'fortiview': 'enable', + 'pdf_report': 'enable', + 'report_source': 'forward-traffic', + 'top_n': '6', + 'web_browsing_threshold': '7' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_setting.fortios_report(input_data, fos_instance) + + expected_data = { + 'fortiview': 'enable', + 'pdf-report': 'enable', + 'report-source': 'forward-traffic', + 'top-n': '6', + 'web-browsing-threshold': '7' + } + + set_method_mock.assert_called_with('report', 'setting', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_report_setting_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_setting': { + 'random_attribute_not_valid': 'tag', + 'fortiview': 'enable', + 'pdf_report': 'enable', + 'report_source': 'forward-traffic', + 'top_n': '6', + 'web_browsing_threshold': '7' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_setting.fortios_report(input_data, fos_instance) + + expected_data = { + 'fortiview': 'enable', + 'pdf-report': 'enable', + 'report-source': 'forward-traffic', + 'top-n': '6', + 'web-browsing-threshold': '7' + } + + set_method_mock.assert_called_with('report', 'setting', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_report_style.py b/test/units/modules/network/fortios/test_fortios_report_style.py new file mode 100644 index 00000000000..bf97f0be03b --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_report_style.py @@ -0,0 +1,449 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_report_style +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_report_style.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_report_style_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_style': { + 'align': 'left', + 'bg_color': 'test_value_4', + 'border_bottom': 'test_value_5', + 'border_left': 'test_value_6', + 'border_right': 'test_value_7', + 'border_top': 'test_value_8', + 'column_gap': 'test_value_9', + 'column_span': 'none', + 'fg_color': 'test_value_11', + 'font_family': 'Verdana', + 'font_size': 'test_value_13', + 'font_style': 'normal', + 'font_weight': 'normal', + 'height': 'test_value_16', + 'line_height': 'test_value_17', + 'margin_bottom': 'test_value_18', + 'margin_left': 'test_value_19', + 'margin_right': 'test_value_20', + 'margin_top': 'test_value_21', + 'name': 'default_name_22', + 'options': 'font', + 'padding_bottom': 'test_value_24', + 'padding_left': 'test_value_25', + 'padding_right': 'test_value_26', + 'padding_top': 'test_value_27', + 'width': 'test_value_28' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_style.fortios_report(input_data, fos_instance) + + expected_data = { + 'align': 'left', + 'bg-color': 'test_value_4', + 'border-bottom': 'test_value_5', + 'border-left': 'test_value_6', + 'border-right': 'test_value_7', + 'border-top': 'test_value_8', + 'column-gap': 'test_value_9', + 'column-span': 'none', + 'fg-color': 'test_value_11', + 'font-family': 'Verdana', + 'font-size': 'test_value_13', + 'font-style': 'normal', + 'font-weight': 'normal', + 'height': 'test_value_16', + 'line-height': 'test_value_17', + 'margin-bottom': 'test_value_18', + 'margin-left': 'test_value_19', + 'margin-right': 'test_value_20', + 'margin-top': 'test_value_21', + 'name': 'default_name_22', + 'options': 'font', + 'padding-bottom': 'test_value_24', + 'padding-left': 'test_value_25', + 'padding-right': 'test_value_26', + 'padding-top': 'test_value_27', + 'width': 'test_value_28' + } + + set_method_mock.assert_called_with('report', 'style', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_report_style_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_style': { + 'align': 'left', + 'bg_color': 'test_value_4', + 'border_bottom': 'test_value_5', + 'border_left': 'test_value_6', + 'border_right': 'test_value_7', + 'border_top': 'test_value_8', + 'column_gap': 'test_value_9', + 'column_span': 'none', + 'fg_color': 'test_value_11', + 'font_family': 'Verdana', + 'font_size': 'test_value_13', + 'font_style': 'normal', + 'font_weight': 'normal', + 'height': 'test_value_16', + 'line_height': 'test_value_17', + 'margin_bottom': 'test_value_18', + 'margin_left': 'test_value_19', + 'margin_right': 'test_value_20', + 'margin_top': 'test_value_21', + 'name': 'default_name_22', + 'options': 'font', + 'padding_bottom': 'test_value_24', + 'padding_left': 'test_value_25', + 'padding_right': 'test_value_26', + 'padding_top': 'test_value_27', + 'width': 'test_value_28' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_style.fortios_report(input_data, fos_instance) + + expected_data = { + 'align': 'left', + 'bg-color': 'test_value_4', + 'border-bottom': 'test_value_5', + 'border-left': 'test_value_6', + 'border-right': 'test_value_7', + 'border-top': 'test_value_8', + 'column-gap': 'test_value_9', + 'column-span': 'none', + 'fg-color': 'test_value_11', + 'font-family': 'Verdana', + 'font-size': 'test_value_13', + 'font-style': 'normal', + 'font-weight': 'normal', + 'height': 'test_value_16', + 'line-height': 'test_value_17', + 'margin-bottom': 'test_value_18', + 'margin-left': 'test_value_19', + 'margin-right': 'test_value_20', + 'margin-top': 'test_value_21', + 'name': 'default_name_22', + 'options': 'font', + 'padding-bottom': 'test_value_24', + 'padding-left': 'test_value_25', + 'padding-right': 'test_value_26', + 'padding-top': 'test_value_27', + 'width': 'test_value_28' + } + + set_method_mock.assert_called_with('report', 'style', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_report_style_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'report_style': { + 'align': 'left', + 'bg_color': 'test_value_4', + 'border_bottom': 'test_value_5', + 'border_left': 'test_value_6', + 'border_right': 'test_value_7', + 'border_top': 'test_value_8', + 'column_gap': 'test_value_9', + 'column_span': 'none', + 'fg_color': 'test_value_11', + 'font_family': 'Verdana', + 'font_size': 'test_value_13', + 'font_style': 'normal', + 'font_weight': 'normal', + 'height': 'test_value_16', + 'line_height': 'test_value_17', + 'margin_bottom': 'test_value_18', + 'margin_left': 'test_value_19', + 'margin_right': 'test_value_20', + 'margin_top': 'test_value_21', + 'name': 'default_name_22', + 'options': 'font', + 'padding_bottom': 'test_value_24', + 'padding_left': 'test_value_25', + 'padding_right': 'test_value_26', + 'padding_top': 'test_value_27', + 'width': 'test_value_28' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_style.fortios_report(input_data, fos_instance) + + delete_method_mock.assert_called_with('report', 'style', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_report_style_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'report_style': { + 'align': 'left', + 'bg_color': 'test_value_4', + 'border_bottom': 'test_value_5', + 'border_left': 'test_value_6', + 'border_right': 'test_value_7', + 'border_top': 'test_value_8', + 'column_gap': 'test_value_9', + 'column_span': 'none', + 'fg_color': 'test_value_11', + 'font_family': 'Verdana', + 'font_size': 'test_value_13', + 'font_style': 'normal', + 'font_weight': 'normal', + 'height': 'test_value_16', + 'line_height': 'test_value_17', + 'margin_bottom': 'test_value_18', + 'margin_left': 'test_value_19', + 'margin_right': 'test_value_20', + 'margin_top': 'test_value_21', + 'name': 'default_name_22', + 'options': 'font', + 'padding_bottom': 'test_value_24', + 'padding_left': 'test_value_25', + 'padding_right': 'test_value_26', + 'padding_top': 'test_value_27', + 'width': 'test_value_28' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_style.fortios_report(input_data, fos_instance) + + delete_method_mock.assert_called_with('report', 'style', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_report_style_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_style': { + 'align': 'left', + 'bg_color': 'test_value_4', + 'border_bottom': 'test_value_5', + 'border_left': 'test_value_6', + 'border_right': 'test_value_7', + 'border_top': 'test_value_8', + 'column_gap': 'test_value_9', + 'column_span': 'none', + 'fg_color': 'test_value_11', + 'font_family': 'Verdana', + 'font_size': 'test_value_13', + 'font_style': 'normal', + 'font_weight': 'normal', + 'height': 'test_value_16', + 'line_height': 'test_value_17', + 'margin_bottom': 'test_value_18', + 'margin_left': 'test_value_19', + 'margin_right': 'test_value_20', + 'margin_top': 'test_value_21', + 'name': 'default_name_22', + 'options': 'font', + 'padding_bottom': 'test_value_24', + 'padding_left': 'test_value_25', + 'padding_right': 'test_value_26', + 'padding_top': 'test_value_27', + 'width': 'test_value_28' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_style.fortios_report(input_data, fos_instance) + + expected_data = { + 'align': 'left', + 'bg-color': 'test_value_4', + 'border-bottom': 'test_value_5', + 'border-left': 'test_value_6', + 'border-right': 'test_value_7', + 'border-top': 'test_value_8', + 'column-gap': 'test_value_9', + 'column-span': 'none', + 'fg-color': 'test_value_11', + 'font-family': 'Verdana', + 'font-size': 'test_value_13', + 'font-style': 'normal', + 'font-weight': 'normal', + 'height': 'test_value_16', + 'line-height': 'test_value_17', + 'margin-bottom': 'test_value_18', + 'margin-left': 'test_value_19', + 'margin-right': 'test_value_20', + 'margin-top': 'test_value_21', + 'name': 'default_name_22', + 'options': 'font', + 'padding-bottom': 'test_value_24', + 'padding-left': 'test_value_25', + 'padding-right': 'test_value_26', + 'padding-top': 'test_value_27', + 'width': 'test_value_28' + } + + set_method_mock.assert_called_with('report', 'style', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_report_style_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_style': { + 'random_attribute_not_valid': 'tag', + 'align': 'left', + 'bg_color': 'test_value_4', + 'border_bottom': 'test_value_5', + 'border_left': 'test_value_6', + 'border_right': 'test_value_7', + 'border_top': 'test_value_8', + 'column_gap': 'test_value_9', + 'column_span': 'none', + 'fg_color': 'test_value_11', + 'font_family': 'Verdana', + 'font_size': 'test_value_13', + 'font_style': 'normal', + 'font_weight': 'normal', + 'height': 'test_value_16', + 'line_height': 'test_value_17', + 'margin_bottom': 'test_value_18', + 'margin_left': 'test_value_19', + 'margin_right': 'test_value_20', + 'margin_top': 'test_value_21', + 'name': 'default_name_22', + 'options': 'font', + 'padding_bottom': 'test_value_24', + 'padding_left': 'test_value_25', + 'padding_right': 'test_value_26', + 'padding_top': 'test_value_27', + 'width': 'test_value_28' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_style.fortios_report(input_data, fos_instance) + + expected_data = { + 'align': 'left', + 'bg-color': 'test_value_4', + 'border-bottom': 'test_value_5', + 'border-left': 'test_value_6', + 'border-right': 'test_value_7', + 'border-top': 'test_value_8', + 'column-gap': 'test_value_9', + 'column-span': 'none', + 'fg-color': 'test_value_11', + 'font-family': 'Verdana', + 'font-size': 'test_value_13', + 'font-style': 'normal', + 'font-weight': 'normal', + 'height': 'test_value_16', + 'line-height': 'test_value_17', + 'margin-bottom': 'test_value_18', + 'margin-left': 'test_value_19', + 'margin-right': 'test_value_20', + 'margin-top': 'test_value_21', + 'name': 'default_name_22', + 'options': 'font', + 'padding-bottom': 'test_value_24', + 'padding-left': 'test_value_25', + 'padding-right': 'test_value_26', + 'padding-top': 'test_value_27', + 'width': 'test_value_28' + } + + set_method_mock.assert_called_with('report', 'style', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_report_theme.py b/test/units/modules/network/fortios/test_fortios_report_theme.py new file mode 100644 index 00000000000..2164b3da61e --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_report_theme.py @@ -0,0 +1,489 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_report_theme +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_report_theme.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_report_theme_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_theme': { + 'bullet_list_style': 'test_value_3', + 'column_count': '1', + 'default_html_style': 'test_value_5', + 'default_pdf_style': 'test_value_6', + 'graph_chart_style': 'test_value_7', + 'heading1_style': 'test_value_8', + 'heading2_style': 'test_value_9', + 'heading3_style': 'test_value_10', + 'heading4_style': 'test_value_11', + 'hline_style': 'test_value_12', + 'image_style': 'test_value_13', + 'name': 'default_name_14', + 'normal_text_style': 'test_value_15', + 'numbered_list_style': 'test_value_16', + 'page_footer_style': 'test_value_17', + 'page_header_style': 'test_value_18', + 'page_orient': 'portrait', + 'page_style': 'test_value_20', + 'report_subtitle_style': 'test_value_21', + 'report_title_style': 'test_value_22', + 'table_chart_caption_style': 'test_value_23', + 'table_chart_even_row_style': 'test_value_24', + 'table_chart_head_style': 'test_value_25', + 'table_chart_odd_row_style': 'test_value_26', + 'table_chart_style': 'test_value_27', + 'toc_heading1_style': 'test_value_28', + 'toc_heading2_style': 'test_value_29', + 'toc_heading3_style': 'test_value_30', + 'toc_heading4_style': 'test_value_31', + 'toc_title_style': 'test_value_32' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_theme.fortios_report(input_data, fos_instance) + + expected_data = { + 'bullet-list-style': 'test_value_3', + 'column-count': '1', + 'default-html-style': 'test_value_5', + 'default-pdf-style': 'test_value_6', + 'graph-chart-style': 'test_value_7', + 'heading1-style': 'test_value_8', + 'heading2-style': 'test_value_9', + 'heading3-style': 'test_value_10', + 'heading4-style': 'test_value_11', + 'hline-style': 'test_value_12', + 'image-style': 'test_value_13', + 'name': 'default_name_14', + 'normal-text-style': 'test_value_15', + 'numbered-list-style': 'test_value_16', + 'page-footer-style': 'test_value_17', + 'page-header-style': 'test_value_18', + 'page-orient': 'portrait', + 'page-style': 'test_value_20', + 'report-subtitle-style': 'test_value_21', + 'report-title-style': 'test_value_22', + 'table-chart-caption-style': 'test_value_23', + 'table-chart-even-row-style': 'test_value_24', + 'table-chart-head-style': 'test_value_25', + 'table-chart-odd-row-style': 'test_value_26', + 'table-chart-style': 'test_value_27', + 'toc-heading1-style': 'test_value_28', + 'toc-heading2-style': 'test_value_29', + 'toc-heading3-style': 'test_value_30', + 'toc-heading4-style': 'test_value_31', + 'toc-title-style': 'test_value_32' + } + + set_method_mock.assert_called_with('report', 'theme', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_report_theme_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_theme': { + 'bullet_list_style': 'test_value_3', + 'column_count': '1', + 'default_html_style': 'test_value_5', + 'default_pdf_style': 'test_value_6', + 'graph_chart_style': 'test_value_7', + 'heading1_style': 'test_value_8', + 'heading2_style': 'test_value_9', + 'heading3_style': 'test_value_10', + 'heading4_style': 'test_value_11', + 'hline_style': 'test_value_12', + 'image_style': 'test_value_13', + 'name': 'default_name_14', + 'normal_text_style': 'test_value_15', + 'numbered_list_style': 'test_value_16', + 'page_footer_style': 'test_value_17', + 'page_header_style': 'test_value_18', + 'page_orient': 'portrait', + 'page_style': 'test_value_20', + 'report_subtitle_style': 'test_value_21', + 'report_title_style': 'test_value_22', + 'table_chart_caption_style': 'test_value_23', + 'table_chart_even_row_style': 'test_value_24', + 'table_chart_head_style': 'test_value_25', + 'table_chart_odd_row_style': 'test_value_26', + 'table_chart_style': 'test_value_27', + 'toc_heading1_style': 'test_value_28', + 'toc_heading2_style': 'test_value_29', + 'toc_heading3_style': 'test_value_30', + 'toc_heading4_style': 'test_value_31', + 'toc_title_style': 'test_value_32' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_theme.fortios_report(input_data, fos_instance) + + expected_data = { + 'bullet-list-style': 'test_value_3', + 'column-count': '1', + 'default-html-style': 'test_value_5', + 'default-pdf-style': 'test_value_6', + 'graph-chart-style': 'test_value_7', + 'heading1-style': 'test_value_8', + 'heading2-style': 'test_value_9', + 'heading3-style': 'test_value_10', + 'heading4-style': 'test_value_11', + 'hline-style': 'test_value_12', + 'image-style': 'test_value_13', + 'name': 'default_name_14', + 'normal-text-style': 'test_value_15', + 'numbered-list-style': 'test_value_16', + 'page-footer-style': 'test_value_17', + 'page-header-style': 'test_value_18', + 'page-orient': 'portrait', + 'page-style': 'test_value_20', + 'report-subtitle-style': 'test_value_21', + 'report-title-style': 'test_value_22', + 'table-chart-caption-style': 'test_value_23', + 'table-chart-even-row-style': 'test_value_24', + 'table-chart-head-style': 'test_value_25', + 'table-chart-odd-row-style': 'test_value_26', + 'table-chart-style': 'test_value_27', + 'toc-heading1-style': 'test_value_28', + 'toc-heading2-style': 'test_value_29', + 'toc-heading3-style': 'test_value_30', + 'toc-heading4-style': 'test_value_31', + 'toc-title-style': 'test_value_32' + } + + set_method_mock.assert_called_with('report', 'theme', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_report_theme_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'report_theme': { + 'bullet_list_style': 'test_value_3', + 'column_count': '1', + 'default_html_style': 'test_value_5', + 'default_pdf_style': 'test_value_6', + 'graph_chart_style': 'test_value_7', + 'heading1_style': 'test_value_8', + 'heading2_style': 'test_value_9', + 'heading3_style': 'test_value_10', + 'heading4_style': 'test_value_11', + 'hline_style': 'test_value_12', + 'image_style': 'test_value_13', + 'name': 'default_name_14', + 'normal_text_style': 'test_value_15', + 'numbered_list_style': 'test_value_16', + 'page_footer_style': 'test_value_17', + 'page_header_style': 'test_value_18', + 'page_orient': 'portrait', + 'page_style': 'test_value_20', + 'report_subtitle_style': 'test_value_21', + 'report_title_style': 'test_value_22', + 'table_chart_caption_style': 'test_value_23', + 'table_chart_even_row_style': 'test_value_24', + 'table_chart_head_style': 'test_value_25', + 'table_chart_odd_row_style': 'test_value_26', + 'table_chart_style': 'test_value_27', + 'toc_heading1_style': 'test_value_28', + 'toc_heading2_style': 'test_value_29', + 'toc_heading3_style': 'test_value_30', + 'toc_heading4_style': 'test_value_31', + 'toc_title_style': 'test_value_32' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_theme.fortios_report(input_data, fos_instance) + + delete_method_mock.assert_called_with('report', 'theme', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_report_theme_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'report_theme': { + 'bullet_list_style': 'test_value_3', + 'column_count': '1', + 'default_html_style': 'test_value_5', + 'default_pdf_style': 'test_value_6', + 'graph_chart_style': 'test_value_7', + 'heading1_style': 'test_value_8', + 'heading2_style': 'test_value_9', + 'heading3_style': 'test_value_10', + 'heading4_style': 'test_value_11', + 'hline_style': 'test_value_12', + 'image_style': 'test_value_13', + 'name': 'default_name_14', + 'normal_text_style': 'test_value_15', + 'numbered_list_style': 'test_value_16', + 'page_footer_style': 'test_value_17', + 'page_header_style': 'test_value_18', + 'page_orient': 'portrait', + 'page_style': 'test_value_20', + 'report_subtitle_style': 'test_value_21', + 'report_title_style': 'test_value_22', + 'table_chart_caption_style': 'test_value_23', + 'table_chart_even_row_style': 'test_value_24', + 'table_chart_head_style': 'test_value_25', + 'table_chart_odd_row_style': 'test_value_26', + 'table_chart_style': 'test_value_27', + 'toc_heading1_style': 'test_value_28', + 'toc_heading2_style': 'test_value_29', + 'toc_heading3_style': 'test_value_30', + 'toc_heading4_style': 'test_value_31', + 'toc_title_style': 'test_value_32' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_theme.fortios_report(input_data, fos_instance) + + delete_method_mock.assert_called_with('report', 'theme', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_report_theme_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_theme': { + 'bullet_list_style': 'test_value_3', + 'column_count': '1', + 'default_html_style': 'test_value_5', + 'default_pdf_style': 'test_value_6', + 'graph_chart_style': 'test_value_7', + 'heading1_style': 'test_value_8', + 'heading2_style': 'test_value_9', + 'heading3_style': 'test_value_10', + 'heading4_style': 'test_value_11', + 'hline_style': 'test_value_12', + 'image_style': 'test_value_13', + 'name': 'default_name_14', + 'normal_text_style': 'test_value_15', + 'numbered_list_style': 'test_value_16', + 'page_footer_style': 'test_value_17', + 'page_header_style': 'test_value_18', + 'page_orient': 'portrait', + 'page_style': 'test_value_20', + 'report_subtitle_style': 'test_value_21', + 'report_title_style': 'test_value_22', + 'table_chart_caption_style': 'test_value_23', + 'table_chart_even_row_style': 'test_value_24', + 'table_chart_head_style': 'test_value_25', + 'table_chart_odd_row_style': 'test_value_26', + 'table_chart_style': 'test_value_27', + 'toc_heading1_style': 'test_value_28', + 'toc_heading2_style': 'test_value_29', + 'toc_heading3_style': 'test_value_30', + 'toc_heading4_style': 'test_value_31', + 'toc_title_style': 'test_value_32' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_theme.fortios_report(input_data, fos_instance) + + expected_data = { + 'bullet-list-style': 'test_value_3', + 'column-count': '1', + 'default-html-style': 'test_value_5', + 'default-pdf-style': 'test_value_6', + 'graph-chart-style': 'test_value_7', + 'heading1-style': 'test_value_8', + 'heading2-style': 'test_value_9', + 'heading3-style': 'test_value_10', + 'heading4-style': 'test_value_11', + 'hline-style': 'test_value_12', + 'image-style': 'test_value_13', + 'name': 'default_name_14', + 'normal-text-style': 'test_value_15', + 'numbered-list-style': 'test_value_16', + 'page-footer-style': 'test_value_17', + 'page-header-style': 'test_value_18', + 'page-orient': 'portrait', + 'page-style': 'test_value_20', + 'report-subtitle-style': 'test_value_21', + 'report-title-style': 'test_value_22', + 'table-chart-caption-style': 'test_value_23', + 'table-chart-even-row-style': 'test_value_24', + 'table-chart-head-style': 'test_value_25', + 'table-chart-odd-row-style': 'test_value_26', + 'table-chart-style': 'test_value_27', + 'toc-heading1-style': 'test_value_28', + 'toc-heading2-style': 'test_value_29', + 'toc-heading3-style': 'test_value_30', + 'toc-heading4-style': 'test_value_31', + 'toc-title-style': 'test_value_32' + } + + set_method_mock.assert_called_with('report', 'theme', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_report_theme_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'report_theme': { + 'random_attribute_not_valid': 'tag', + 'bullet_list_style': 'test_value_3', + 'column_count': '1', + 'default_html_style': 'test_value_5', + 'default_pdf_style': 'test_value_6', + 'graph_chart_style': 'test_value_7', + 'heading1_style': 'test_value_8', + 'heading2_style': 'test_value_9', + 'heading3_style': 'test_value_10', + 'heading4_style': 'test_value_11', + 'hline_style': 'test_value_12', + 'image_style': 'test_value_13', + 'name': 'default_name_14', + 'normal_text_style': 'test_value_15', + 'numbered_list_style': 'test_value_16', + 'page_footer_style': 'test_value_17', + 'page_header_style': 'test_value_18', + 'page_orient': 'portrait', + 'page_style': 'test_value_20', + 'report_subtitle_style': 'test_value_21', + 'report_title_style': 'test_value_22', + 'table_chart_caption_style': 'test_value_23', + 'table_chart_even_row_style': 'test_value_24', + 'table_chart_head_style': 'test_value_25', + 'table_chart_odd_row_style': 'test_value_26', + 'table_chart_style': 'test_value_27', + 'toc_heading1_style': 'test_value_28', + 'toc_heading2_style': 'test_value_29', + 'toc_heading3_style': 'test_value_30', + 'toc_heading4_style': 'test_value_31', + 'toc_title_style': 'test_value_32' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_report_theme.fortios_report(input_data, fos_instance) + + expected_data = { + 'bullet-list-style': 'test_value_3', + 'column-count': '1', + 'default-html-style': 'test_value_5', + 'default-pdf-style': 'test_value_6', + 'graph-chart-style': 'test_value_7', + 'heading1-style': 'test_value_8', + 'heading2-style': 'test_value_9', + 'heading3-style': 'test_value_10', + 'heading4-style': 'test_value_11', + 'hline-style': 'test_value_12', + 'image-style': 'test_value_13', + 'name': 'default_name_14', + 'normal-text-style': 'test_value_15', + 'numbered-list-style': 'test_value_16', + 'page-footer-style': 'test_value_17', + 'page-header-style': 'test_value_18', + 'page-orient': 'portrait', + 'page-style': 'test_value_20', + 'report-subtitle-style': 'test_value_21', + 'report-title-style': 'test_value_22', + 'table-chart-caption-style': 'test_value_23', + 'table-chart-even-row-style': 'test_value_24', + 'table-chart-head-style': 'test_value_25', + 'table-chart-odd-row-style': 'test_value_26', + 'table-chart-style': 'test_value_27', + 'toc-heading1-style': 'test_value_28', + 'toc-heading2-style': 'test_value_29', + 'toc-heading3-style': 'test_value_30', + 'toc-heading4-style': 'test_value_31', + 'toc-title-style': 'test_value_32' + } + + set_method_mock.assert_called_with('report', 'theme', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_access_list.py b/test/units/modules/network/fortios/test_fortios_router_access_list.py new file mode 100644 index 00000000000..f0cf338ba6f --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_access_list.py @@ -0,0 +1,219 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_access_list +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_access_list.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_access_list_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_access_list': { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_access_list.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + } + + set_method_mock.assert_called_with('router', 'access-list', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_access_list_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_access_list': { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_access_list.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + } + + set_method_mock.assert_called_with('router', 'access-list', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_access_list_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'router_access_list': { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_access_list.fortios_router(input_data, fos_instance) + + delete_method_mock.assert_called_with('router', 'access-list', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_access_list_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'router_access_list': { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_access_list.fortios_router(input_data, fos_instance) + + delete_method_mock.assert_called_with('router', 'access-list', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_access_list_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_access_list': { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_access_list.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + } + + set_method_mock.assert_called_with('router', 'access-list', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_access_list_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_access_list': { + 'random_attribute_not_valid': 'tag', + 'comments': 'test_value_3', + 'name': 'default_name_4', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_access_list.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + } + + set_method_mock.assert_called_with('router', 'access-list', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_auth_path.py b/test/units/modules/network/fortios/test_fortios_router_auth_path.py new file mode 100644 index 00000000000..8762f7d60be --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_auth_path.py @@ -0,0 +1,219 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_auth_path +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_auth_path.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_auth_path_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_auth_path': { + 'device': 'test_value_3', + 'gateway': 'test_value_4', + 'name': 'default_name_5' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_auth_path.fortios_router(input_data, fos_instance) + + expected_data = { + 'device': 'test_value_3', + 'gateway': 'test_value_4', + 'name': 'default_name_5' + } + + set_method_mock.assert_called_with('router', 'auth-path', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_auth_path_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_auth_path': { + 'device': 'test_value_3', + 'gateway': 'test_value_4', + 'name': 'default_name_5' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_auth_path.fortios_router(input_data, fos_instance) + + expected_data = { + 'device': 'test_value_3', + 'gateway': 'test_value_4', + 'name': 'default_name_5' + } + + set_method_mock.assert_called_with('router', 'auth-path', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_auth_path_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'router_auth_path': { + 'device': 'test_value_3', + 'gateway': 'test_value_4', + 'name': 'default_name_5' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_auth_path.fortios_router(input_data, fos_instance) + + delete_method_mock.assert_called_with('router', 'auth-path', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_auth_path_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'router_auth_path': { + 'device': 'test_value_3', + 'gateway': 'test_value_4', + 'name': 'default_name_5' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_auth_path.fortios_router(input_data, fos_instance) + + delete_method_mock.assert_called_with('router', 'auth-path', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_auth_path_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_auth_path': { + 'device': 'test_value_3', + 'gateway': 'test_value_4', + 'name': 'default_name_5' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_auth_path.fortios_router(input_data, fos_instance) + + expected_data = { + 'device': 'test_value_3', + 'gateway': 'test_value_4', + 'name': 'default_name_5' + } + + set_method_mock.assert_called_with('router', 'auth-path', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_auth_path_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_auth_path': { + 'random_attribute_not_valid': 'tag', + 'device': 'test_value_3', + 'gateway': 'test_value_4', + 'name': 'default_name_5' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_auth_path.fortios_router(input_data, fos_instance) + + expected_data = { + 'device': 'test_value_3', + 'gateway': 'test_value_4', + 'name': 'default_name_5' + } + + set_method_mock.assert_called_with('router', 'auth-path', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_bfd.py b/test/units/modules/network/fortios/test_fortios_router_bfd.py new file mode 100644 index 00000000000..d462853d904 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_bfd.py @@ -0,0 +1,143 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_bfd +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_bfd.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_bfd_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_bfd': { + 'neighbor': [{'interface': 'if1', 'ip': '10.20.10.10'}] + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_bfd.fortios_router(input_data, fos_instance) + + expected_data = {'neighbor': [{'interface': 'if1', 'ip': '10.20.10.10'}]} + + set_method_mock.assert_called_with('router', 'bfd', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_bfd_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_bfd': { + 'neighbor': [{'interface': 'if1', 'ip': '10.20.10.10'}] + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_bfd.fortios_router(input_data, fos_instance) + + expected_data = {'neighbor': [{'interface': 'if1', 'ip': '10.20.10.10'}]} + + set_method_mock.assert_called_with('router', 'bfd', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_bfd_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_bfd': { + 'neighbor': [{'interface': 'if1', 'ip': '10.20.10.10'}] + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_bfd.fortios_router(input_data, fos_instance) + + expected_data = {'neighbor': [{'interface': 'if1', 'ip': '10.20.10.10'}]} + + set_method_mock.assert_called_with('router', 'bfd', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_bfd_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_bfd': { + 'random_attribute_not_valid': 'tag', + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_bfd.fortios_router(input_data, fos_instance) + + expected_data = { + } + + set_method_mock.assert_called_with('router', 'bfd', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_bfd6.py b/test/units/modules/network/fortios/test_fortios_router_bfd6.py new file mode 100644 index 00000000000..94b6e3d7fb1 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_bfd6.py @@ -0,0 +1,143 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_bfd6 +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_bfd6.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_bfd6_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_bfd6': { + 'neighbor': [{'interface': 'if1', 'ip': '10.20.10.10'}] + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_bfd6.fortios_router(input_data, fos_instance) + + expected_data = {'neighbor': [{'interface': 'if1', 'ip': '10.20.10.10'}]} + + set_method_mock.assert_called_with('router', 'bfd6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_bfd6_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_bfd6': { + 'neighbor': [{'interface': 'if1', 'ip': '10.20.10.10'}] + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_bfd6.fortios_router(input_data, fos_instance) + + expected_data = {'neighbor': [{'interface': 'if1', 'ip': '10.20.10.10'}]} + + set_method_mock.assert_called_with('router', 'bfd6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_bfd6_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_bfd6': { + 'neighbor': [{'interface': 'if1', 'ip': '10.20.10.10'}] + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_bfd6.fortios_router(input_data, fos_instance) + + expected_data = {'neighbor': [{'interface': 'if1', 'ip': '10.20.10.10'}]} + + set_method_mock.assert_called_with('router', 'bfd6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_bfd6_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_bfd6': { + 'random_attribute_not_valid': 'tag', + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_bfd6.fortios_router(input_data, fos_instance) + + expected_data = { + } + + set_method_mock.assert_called_with('router', 'bfd6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_bgp.py b/test/units/modules/network/fortios/test_fortios_router_bgp.py new file mode 100644 index 00000000000..0d7617ee7ad --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_bgp.py @@ -0,0 +1,447 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_bgp +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_bgp.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_bgp_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_bgp': {'always_compare_med': 'enable', + 'as': '4', + 'bestpath_as_path_ignore': 'enable', + 'bestpath_cmp_confed_aspath': 'enable', + 'bestpath_cmp_routerid': 'enable', + 'bestpath_med_confed': 'enable', + 'bestpath_med_missing_as_worst': 'enable', + 'client_to_client_reflection': 'enable', + 'cluster_id': 'test_value_11', + 'confederation_identifier': '12', + 'dampening': 'enable', + 'dampening_max_suppress_time': '14', + 'dampening_reachability_half_life': '15', + 'dampening_reuse': '16', + 'dampening_route_map': 'test_value_17', + 'dampening_suppress': '18', + 'dampening_unreachability_half_life': '19', + 'default_local_preference': '20', + 'deterministic_med': 'enable', + 'distance_external': '22', + 'distance_internal': '23', + 'distance_local': '24', + 'ebgp_multipath': 'enable', + 'enforce_first_as': 'enable', + 'fast_external_failover': 'enable', + 'graceful_end_on_timer': 'enable', + 'graceful_restart': 'enable', + 'graceful_restart_time': '30', + 'graceful_stalepath_time': '31', + 'graceful_update_delay': '32', + 'holdtime_timer': '33', + 'ibgp_multipath': 'enable', + 'ignore_optional_capability': 'enable', + 'keepalive_timer': '36', + 'log_neighbour_changes': 'enable', + 'network_import_check': 'enable', + 'router_id': 'test_value_39', + 'scan_time': '40', + 'synchronization': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_bgp.fortios_router(input_data, fos_instance) + + expected_data = {'always-compare-med': 'enable', + 'as': '4', + 'bestpath-as-path-ignore': 'enable', + 'bestpath-cmp-confed-aspath': 'enable', + 'bestpath-cmp-routerid': 'enable', + 'bestpath-med-confed': 'enable', + 'bestpath-med-missing-as-worst': 'enable', + 'client-to-client-reflection': 'enable', + 'cluster-id': 'test_value_11', + 'confederation-identifier': '12', + 'dampening': 'enable', + 'dampening-max-suppress-time': '14', + 'dampening-reachability-half-life': '15', + 'dampening-reuse': '16', + 'dampening-route-map': 'test_value_17', + 'dampening-suppress': '18', + 'dampening-unreachability-half-life': '19', + 'default-local-preference': '20', + 'deterministic-med': 'enable', + 'distance-external': '22', + 'distance-internal': '23', + 'distance-local': '24', + 'ebgp-multipath': 'enable', + 'enforce-first-as': 'enable', + 'fast-external-failover': 'enable', + 'graceful-end-on-timer': 'enable', + 'graceful-restart': 'enable', + 'graceful-restart-time': '30', + 'graceful-stalepath-time': '31', + 'graceful-update-delay': '32', + 'holdtime-timer': '33', + 'ibgp-multipath': 'enable', + 'ignore-optional-capability': 'enable', + 'keepalive-timer': '36', + 'log-neighbour-changes': 'enable', + 'network-import-check': 'enable', + 'router-id': 'test_value_39', + 'scan-time': '40', + 'synchronization': 'enable' + } + + set_method_mock.assert_called_with('router', 'bgp', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_bgp_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_bgp': {'always_compare_med': 'enable', + 'as': '4', + 'bestpath_as_path_ignore': 'enable', + 'bestpath_cmp_confed_aspath': 'enable', + 'bestpath_cmp_routerid': 'enable', + 'bestpath_med_confed': 'enable', + 'bestpath_med_missing_as_worst': 'enable', + 'client_to_client_reflection': 'enable', + 'cluster_id': 'test_value_11', + 'confederation_identifier': '12', + 'dampening': 'enable', + 'dampening_max_suppress_time': '14', + 'dampening_reachability_half_life': '15', + 'dampening_reuse': '16', + 'dampening_route_map': 'test_value_17', + 'dampening_suppress': '18', + 'dampening_unreachability_half_life': '19', + 'default_local_preference': '20', + 'deterministic_med': 'enable', + 'distance_external': '22', + 'distance_internal': '23', + 'distance_local': '24', + 'ebgp_multipath': 'enable', + 'enforce_first_as': 'enable', + 'fast_external_failover': 'enable', + 'graceful_end_on_timer': 'enable', + 'graceful_restart': 'enable', + 'graceful_restart_time': '30', + 'graceful_stalepath_time': '31', + 'graceful_update_delay': '32', + 'holdtime_timer': '33', + 'ibgp_multipath': 'enable', + 'ignore_optional_capability': 'enable', + 'keepalive_timer': '36', + 'log_neighbour_changes': 'enable', + 'network_import_check': 'enable', + 'router_id': 'test_value_39', + 'scan_time': '40', + 'synchronization': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_bgp.fortios_router(input_data, fos_instance) + + expected_data = {'always-compare-med': 'enable', + 'as': '4', + 'bestpath-as-path-ignore': 'enable', + 'bestpath-cmp-confed-aspath': 'enable', + 'bestpath-cmp-routerid': 'enable', + 'bestpath-med-confed': 'enable', + 'bestpath-med-missing-as-worst': 'enable', + 'client-to-client-reflection': 'enable', + 'cluster-id': 'test_value_11', + 'confederation-identifier': '12', + 'dampening': 'enable', + 'dampening-max-suppress-time': '14', + 'dampening-reachability-half-life': '15', + 'dampening-reuse': '16', + 'dampening-route-map': 'test_value_17', + 'dampening-suppress': '18', + 'dampening-unreachability-half-life': '19', + 'default-local-preference': '20', + 'deterministic-med': 'enable', + 'distance-external': '22', + 'distance-internal': '23', + 'distance-local': '24', + 'ebgp-multipath': 'enable', + 'enforce-first-as': 'enable', + 'fast-external-failover': 'enable', + 'graceful-end-on-timer': 'enable', + 'graceful-restart': 'enable', + 'graceful-restart-time': '30', + 'graceful-stalepath-time': '31', + 'graceful-update-delay': '32', + 'holdtime-timer': '33', + 'ibgp-multipath': 'enable', + 'ignore-optional-capability': 'enable', + 'keepalive-timer': '36', + 'log-neighbour-changes': 'enable', + 'network-import-check': 'enable', + 'router-id': 'test_value_39', + 'scan-time': '40', + 'synchronization': 'enable' + } + + set_method_mock.assert_called_with('router', 'bgp', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_bgp_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_bgp': {'always_compare_med': 'enable', + 'as': '4', + 'bestpath_as_path_ignore': 'enable', + 'bestpath_cmp_confed_aspath': 'enable', + 'bestpath_cmp_routerid': 'enable', + 'bestpath_med_confed': 'enable', + 'bestpath_med_missing_as_worst': 'enable', + 'client_to_client_reflection': 'enable', + 'cluster_id': 'test_value_11', + 'confederation_identifier': '12', + 'dampening': 'enable', + 'dampening_max_suppress_time': '14', + 'dampening_reachability_half_life': '15', + 'dampening_reuse': '16', + 'dampening_route_map': 'test_value_17', + 'dampening_suppress': '18', + 'dampening_unreachability_half_life': '19', + 'default_local_preference': '20', + 'deterministic_med': 'enable', + 'distance_external': '22', + 'distance_internal': '23', + 'distance_local': '24', + 'ebgp_multipath': 'enable', + 'enforce_first_as': 'enable', + 'fast_external_failover': 'enable', + 'graceful_end_on_timer': 'enable', + 'graceful_restart': 'enable', + 'graceful_restart_time': '30', + 'graceful_stalepath_time': '31', + 'graceful_update_delay': '32', + 'holdtime_timer': '33', + 'ibgp_multipath': 'enable', + 'ignore_optional_capability': 'enable', + 'keepalive_timer': '36', + 'log_neighbour_changes': 'enable', + 'network_import_check': 'enable', + 'router_id': 'test_value_39', + 'scan_time': '40', + 'synchronization': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_bgp.fortios_router(input_data, fos_instance) + + expected_data = {'always-compare-med': 'enable', + 'as': '4', + 'bestpath-as-path-ignore': 'enable', + 'bestpath-cmp-confed-aspath': 'enable', + 'bestpath-cmp-routerid': 'enable', + 'bestpath-med-confed': 'enable', + 'bestpath-med-missing-as-worst': 'enable', + 'client-to-client-reflection': 'enable', + 'cluster-id': 'test_value_11', + 'confederation-identifier': '12', + 'dampening': 'enable', + 'dampening-max-suppress-time': '14', + 'dampening-reachability-half-life': '15', + 'dampening-reuse': '16', + 'dampening-route-map': 'test_value_17', + 'dampening-suppress': '18', + 'dampening-unreachability-half-life': '19', + 'default-local-preference': '20', + 'deterministic-med': 'enable', + 'distance-external': '22', + 'distance-internal': '23', + 'distance-local': '24', + 'ebgp-multipath': 'enable', + 'enforce-first-as': 'enable', + 'fast-external-failover': 'enable', + 'graceful-end-on-timer': 'enable', + 'graceful-restart': 'enable', + 'graceful-restart-time': '30', + 'graceful-stalepath-time': '31', + 'graceful-update-delay': '32', + 'holdtime-timer': '33', + 'ibgp-multipath': 'enable', + 'ignore-optional-capability': 'enable', + 'keepalive-timer': '36', + 'log-neighbour-changes': 'enable', + 'network-import-check': 'enable', + 'router-id': 'test_value_39', + 'scan-time': '40', + 'synchronization': 'enable' + } + + set_method_mock.assert_called_with('router', 'bgp', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_bgp_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_bgp': { + 'random_attribute_not_valid': 'tag', 'always_compare_med': 'enable', + 'as': '4', + 'bestpath_as_path_ignore': 'enable', + 'bestpath_cmp_confed_aspath': 'enable', + 'bestpath_cmp_routerid': 'enable', + 'bestpath_med_confed': 'enable', + 'bestpath_med_missing_as_worst': 'enable', + 'client_to_client_reflection': 'enable', + 'cluster_id': 'test_value_11', + 'confederation_identifier': '12', + 'dampening': 'enable', + 'dampening_max_suppress_time': '14', + 'dampening_reachability_half_life': '15', + 'dampening_reuse': '16', + 'dampening_route_map': 'test_value_17', + 'dampening_suppress': '18', + 'dampening_unreachability_half_life': '19', + 'default_local_preference': '20', + 'deterministic_med': 'enable', + 'distance_external': '22', + 'distance_internal': '23', + 'distance_local': '24', + 'ebgp_multipath': 'enable', + 'enforce_first_as': 'enable', + 'fast_external_failover': 'enable', + 'graceful_end_on_timer': 'enable', + 'graceful_restart': 'enable', + 'graceful_restart_time': '30', + 'graceful_stalepath_time': '31', + 'graceful_update_delay': '32', + 'holdtime_timer': '33', + 'ibgp_multipath': 'enable', + 'ignore_optional_capability': 'enable', + 'keepalive_timer': '36', + 'log_neighbour_changes': 'enable', + 'network_import_check': 'enable', + 'router_id': 'test_value_39', + 'scan_time': '40', + 'synchronization': 'enable' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_bgp.fortios_router(input_data, fos_instance) + + expected_data = {'always-compare-med': 'enable', + 'as': '4', + 'bestpath-as-path-ignore': 'enable', + 'bestpath-cmp-confed-aspath': 'enable', + 'bestpath-cmp-routerid': 'enable', + 'bestpath-med-confed': 'enable', + 'bestpath-med-missing-as-worst': 'enable', + 'client-to-client-reflection': 'enable', + 'cluster-id': 'test_value_11', + 'confederation-identifier': '12', + 'dampening': 'enable', + 'dampening-max-suppress-time': '14', + 'dampening-reachability-half-life': '15', + 'dampening-reuse': '16', + 'dampening-route-map': 'test_value_17', + 'dampening-suppress': '18', + 'dampening-unreachability-half-life': '19', + 'default-local-preference': '20', + 'deterministic-med': 'enable', + 'distance-external': '22', + 'distance-internal': '23', + 'distance-local': '24', + 'ebgp-multipath': 'enable', + 'enforce-first-as': 'enable', + 'fast-external-failover': 'enable', + 'graceful-end-on-timer': 'enable', + 'graceful-restart': 'enable', + 'graceful-restart-time': '30', + 'graceful-stalepath-time': '31', + 'graceful-update-delay': '32', + 'holdtime-timer': '33', + 'ibgp-multipath': 'enable', + 'ignore-optional-capability': 'enable', + 'keepalive-timer': '36', + 'log-neighbour-changes': 'enable', + 'network-import-check': 'enable', + 'router-id': 'test_value_39', + 'scan-time': '40', + 'synchronization': 'enable' + } + + set_method_mock.assert_called_with('router', 'bgp', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_multicast.py b/test/units/modules/network/fortios/test_fortios_router_multicast.py new file mode 100644 index 00000000000..34e3dc562dd --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_multicast.py @@ -0,0 +1,159 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_multicast +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_multicast.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_multicast_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_multicast': {'multicast_routing': 'enable', + 'route_limit': '4', + 'route_threshold': '5' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_multicast.fortios_router(input_data, fos_instance) + + expected_data = {'multicast-routing': 'enable', + 'route-limit': '4', + 'route-threshold': '5' + } + + set_method_mock.assert_called_with('router', 'multicast', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_multicast_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_multicast': {'multicast_routing': 'enable', + 'route_limit': '4', + 'route_threshold': '5' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_multicast.fortios_router(input_data, fos_instance) + + expected_data = {'multicast-routing': 'enable', + 'route-limit': '4', + 'route-threshold': '5' + } + + set_method_mock.assert_called_with('router', 'multicast', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_multicast_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_multicast': {'multicast_routing': 'enable', + 'route_limit': '4', + 'route_threshold': '5' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_multicast.fortios_router(input_data, fos_instance) + + expected_data = {'multicast-routing': 'enable', + 'route-limit': '4', + 'route-threshold': '5' + } + + set_method_mock.assert_called_with('router', 'multicast', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_multicast_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_multicast': { + 'random_attribute_not_valid': 'tag', 'multicast_routing': 'enable', + 'route_limit': '4', + 'route_threshold': '5' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_multicast.fortios_router(input_data, fos_instance) + + expected_data = {'multicast-routing': 'enable', + 'route-limit': '4', + 'route-threshold': '5' + } + + set_method_mock.assert_called_with('router', 'multicast', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_multicast6.py b/test/units/modules/network/fortios/test_fortios_router_multicast6.py new file mode 100644 index 00000000000..5fb6e25a87f --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_multicast6.py @@ -0,0 +1,159 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_multicast6 +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_multicast6.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_multicast6_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_multicast6': {'multicast_pmtu': 'enable', + 'multicast_routing': 'enable', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_multicast6.fortios_router(input_data, fos_instance) + + expected_data = {'multicast-pmtu': 'enable', + 'multicast-routing': 'enable', + + } + + set_method_mock.assert_called_with('router', 'multicast6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_multicast6_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_multicast6': {'multicast_pmtu': 'enable', + 'multicast_routing': 'enable', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_multicast6.fortios_router(input_data, fos_instance) + + expected_data = {'multicast-pmtu': 'enable', + 'multicast-routing': 'enable', + + } + + set_method_mock.assert_called_with('router', 'multicast6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_multicast6_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_multicast6': {'multicast_pmtu': 'enable', + 'multicast_routing': 'enable', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_multicast6.fortios_router(input_data, fos_instance) + + expected_data = {'multicast-pmtu': 'enable', + 'multicast-routing': 'enable', + + } + + set_method_mock.assert_called_with('router', 'multicast6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_multicast6_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_multicast6': { + 'random_attribute_not_valid': 'tag', 'multicast_pmtu': 'enable', + 'multicast_routing': 'enable', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_multicast6.fortios_router(input_data, fos_instance) + + expected_data = {'multicast-pmtu': 'enable', + 'multicast-routing': 'enable', + + } + + set_method_mock.assert_called_with('router', 'multicast6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_multicast_flow.py b/test/units/modules/network/fortios/test_fortios_router_multicast_flow.py new file mode 100644 index 00000000000..92eada85f32 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_multicast_flow.py @@ -0,0 +1,209 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_multicast_flow +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_multicast_flow.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_multicast_flow_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_multicast_flow': { + 'comments': 'test_value_3', + 'name': 'default_name_4' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_multicast_flow.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'name': 'default_name_4' + } + + set_method_mock.assert_called_with('router', 'multicast-flow', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_multicast_flow_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_multicast_flow': { + 'comments': 'test_value_3', + 'name': 'default_name_4' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_multicast_flow.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'name': 'default_name_4' + } + + set_method_mock.assert_called_with('router', 'multicast-flow', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_multicast_flow_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'router_multicast_flow': { + 'comments': 'test_value_3', + 'name': 'default_name_4' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_multicast_flow.fortios_router(input_data, fos_instance) + + delete_method_mock.assert_called_with('router', 'multicast-flow', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_multicast_flow_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'router_multicast_flow': { + 'comments': 'test_value_3', + 'name': 'default_name_4' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_multicast_flow.fortios_router(input_data, fos_instance) + + delete_method_mock.assert_called_with('router', 'multicast-flow', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_multicast_flow_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_multicast_flow': { + 'comments': 'test_value_3', + 'name': 'default_name_4' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_multicast_flow.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'name': 'default_name_4' + } + + set_method_mock.assert_called_with('router', 'multicast-flow', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_multicast_flow_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_multicast_flow': { + 'random_attribute_not_valid': 'tag', + 'comments': 'test_value_3', + 'name': 'default_name_4' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_multicast_flow.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'name': 'default_name_4' + } + + set_method_mock.assert_called_with('router', 'multicast-flow', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_ospf.py b/test/units/modules/network/fortios/test_fortios_router_ospf.py new file mode 100644 index 00000000000..f7b8565d094 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_ospf.py @@ -0,0 +1,335 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_ospf +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_ospf.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_ospf_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_ospf': { + 'abr_type': 'cisco', + 'auto_cost_ref_bandwidth': '4', + 'bfd': 'enable', + 'database_overflow': 'enable', + 'database_overflow_max_lsas': '7', + 'database_overflow_time_to_recover': '8', + 'default_information_metric': '9', + 'default_information_metric_type': '1', + 'default_information_originate': 'enable', + 'default_information_route_map': 'test_value_12', + 'default_metric': '13', + 'distance': '14', + 'distance_external': '15', + 'distance_inter_area': '16', + 'distance_intra_area': '17', + 'distribute_list_in': 'test_value_18', + 'distribute_route_map_in': 'test_value_19', + 'log_neighbour_changes': 'enable', + 'restart_mode': 'none', + 'restart_period': '22', + 'rfc1583_compatible': 'enable', + 'router_id': 'test_value_24', + 'spf_timers': 'test_value_25', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_ospf.fortios_router(input_data, fos_instance) + + expected_data = { + 'abr-type': 'cisco', + 'auto-cost-ref-bandwidth': '4', + 'bfd': 'enable', + 'database-overflow': 'enable', + 'database-overflow-max-lsas': '7', + 'database-overflow-time-to-recover': '8', + 'default-information-metric': '9', + 'default-information-metric-type': '1', + 'default-information-originate': 'enable', + 'default-information-route-map': 'test_value_12', + 'default-metric': '13', + 'distance': '14', + 'distance-external': '15', + 'distance-inter-area': '16', + 'distance-intra-area': '17', + 'distribute-list-in': 'test_value_18', + 'distribute-route-map-in': 'test_value_19', + 'log-neighbour-changes': 'enable', + 'restart-mode': 'none', + 'restart-period': '22', + 'rfc1583-compatible': 'enable', + 'router-id': 'test_value_24', + 'spf-timers': 'test_value_25', + + } + + set_method_mock.assert_called_with('router', 'ospf', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_ospf_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_ospf': { + 'abr_type': 'cisco', + 'auto_cost_ref_bandwidth': '4', + 'bfd': 'enable', + 'database_overflow': 'enable', + 'database_overflow_max_lsas': '7', + 'database_overflow_time_to_recover': '8', + 'default_information_metric': '9', + 'default_information_metric_type': '1', + 'default_information_originate': 'enable', + 'default_information_route_map': 'test_value_12', + 'default_metric': '13', + 'distance': '14', + 'distance_external': '15', + 'distance_inter_area': '16', + 'distance_intra_area': '17', + 'distribute_list_in': 'test_value_18', + 'distribute_route_map_in': 'test_value_19', + 'log_neighbour_changes': 'enable', + 'restart_mode': 'none', + 'restart_period': '22', + 'rfc1583_compatible': 'enable', + 'router_id': 'test_value_24', + 'spf_timers': 'test_value_25', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_ospf.fortios_router(input_data, fos_instance) + + expected_data = { + 'abr-type': 'cisco', + 'auto-cost-ref-bandwidth': '4', + 'bfd': 'enable', + 'database-overflow': 'enable', + 'database-overflow-max-lsas': '7', + 'database-overflow-time-to-recover': '8', + 'default-information-metric': '9', + 'default-information-metric-type': '1', + 'default-information-originate': 'enable', + 'default-information-route-map': 'test_value_12', + 'default-metric': '13', + 'distance': '14', + 'distance-external': '15', + 'distance-inter-area': '16', + 'distance-intra-area': '17', + 'distribute-list-in': 'test_value_18', + 'distribute-route-map-in': 'test_value_19', + 'log-neighbour-changes': 'enable', + 'restart-mode': 'none', + 'restart-period': '22', + 'rfc1583-compatible': 'enable', + 'router-id': 'test_value_24', + 'spf-timers': 'test_value_25', + + } + + set_method_mock.assert_called_with('router', 'ospf', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_ospf_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_ospf': { + 'abr_type': 'cisco', + 'auto_cost_ref_bandwidth': '4', + 'bfd': 'enable', + 'database_overflow': 'enable', + 'database_overflow_max_lsas': '7', + 'database_overflow_time_to_recover': '8', + 'default_information_metric': '9', + 'default_information_metric_type': '1', + 'default_information_originate': 'enable', + 'default_information_route_map': 'test_value_12', + 'default_metric': '13', + 'distance': '14', + 'distance_external': '15', + 'distance_inter_area': '16', + 'distance_intra_area': '17', + 'distribute_list_in': 'test_value_18', + 'distribute_route_map_in': 'test_value_19', + 'log_neighbour_changes': 'enable', + 'restart_mode': 'none', + 'restart_period': '22', + 'rfc1583_compatible': 'enable', + 'router_id': 'test_value_24', + 'spf_timers': 'test_value_25', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_ospf.fortios_router(input_data, fos_instance) + + expected_data = { + 'abr-type': 'cisco', + 'auto-cost-ref-bandwidth': '4', + 'bfd': 'enable', + 'database-overflow': 'enable', + 'database-overflow-max-lsas': '7', + 'database-overflow-time-to-recover': '8', + 'default-information-metric': '9', + 'default-information-metric-type': '1', + 'default-information-originate': 'enable', + 'default-information-route-map': 'test_value_12', + 'default-metric': '13', + 'distance': '14', + 'distance-external': '15', + 'distance-inter-area': '16', + 'distance-intra-area': '17', + 'distribute-list-in': 'test_value_18', + 'distribute-route-map-in': 'test_value_19', + 'log-neighbour-changes': 'enable', + 'restart-mode': 'none', + 'restart-period': '22', + 'rfc1583-compatible': 'enable', + 'router-id': 'test_value_24', + 'spf-timers': 'test_value_25', + + } + + set_method_mock.assert_called_with('router', 'ospf', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_ospf_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_ospf': { + 'random_attribute_not_valid': 'tag', + 'abr_type': 'cisco', + 'auto_cost_ref_bandwidth': '4', + 'bfd': 'enable', + 'database_overflow': 'enable', + 'database_overflow_max_lsas': '7', + 'database_overflow_time_to_recover': '8', + 'default_information_metric': '9', + 'default_information_metric_type': '1', + 'default_information_originate': 'enable', + 'default_information_route_map': 'test_value_12', + 'default_metric': '13', + 'distance': '14', + 'distance_external': '15', + 'distance_inter_area': '16', + 'distance_intra_area': '17', + 'distribute_list_in': 'test_value_18', + 'distribute_route_map_in': 'test_value_19', + 'log_neighbour_changes': 'enable', + 'restart_mode': 'none', + 'restart_period': '22', + 'rfc1583_compatible': 'enable', + 'router_id': 'test_value_24', + 'spf_timers': 'test_value_25', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_ospf.fortios_router(input_data, fos_instance) + + expected_data = { + 'abr-type': 'cisco', + 'auto-cost-ref-bandwidth': '4', + 'bfd': 'enable', + 'database-overflow': 'enable', + 'database-overflow-max-lsas': '7', + 'database-overflow-time-to-recover': '8', + 'default-information-metric': '9', + 'default-information-metric-type': '1', + 'default-information-originate': 'enable', + 'default-information-route-map': 'test_value_12', + 'default-metric': '13', + 'distance': '14', + 'distance-external': '15', + 'distance-inter-area': '16', + 'distance-intra-area': '17', + 'distribute-list-in': 'test_value_18', + 'distribute-route-map-in': 'test_value_19', + 'log-neighbour-changes': 'enable', + 'restart-mode': 'none', + 'restart-period': '22', + 'rfc1583-compatible': 'enable', + 'router-id': 'test_value_24', + 'spf-timers': 'test_value_25', + + } + + set_method_mock.assert_called_with('router', 'ospf', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_ospf6.py b/test/units/modules/network/fortios/test_fortios_router_ospf6.py new file mode 100644 index 00000000000..73151677419 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_ospf6.py @@ -0,0 +1,239 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_ospf6 +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_ospf6.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_ospf6_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_ospf6': { + 'abr_type': 'cisco', + 'auto_cost_ref_bandwidth': '4', + 'bfd': 'enable', + 'default_information_metric': '6', + 'default_information_metric_type': '1', + 'default_information_originate': 'enable', + 'default_information_route_map': 'test_value_9', + 'default_metric': '10', + 'log_neighbour_changes': 'enable', + 'router_id': 'test_value_12', + 'spf_timers': 'test_value_13', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_ospf6.fortios_router(input_data, fos_instance) + + expected_data = { + 'abr-type': 'cisco', + 'auto-cost-ref-bandwidth': '4', + 'bfd': 'enable', + 'default-information-metric': '6', + 'default-information-metric-type': '1', + 'default-information-originate': 'enable', + 'default-information-route-map': 'test_value_9', + 'default-metric': '10', + 'log-neighbour-changes': 'enable', + 'router-id': 'test_value_12', + 'spf-timers': 'test_value_13', + + } + + set_method_mock.assert_called_with('router', 'ospf6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_ospf6_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_ospf6': { + 'abr_type': 'cisco', + 'auto_cost_ref_bandwidth': '4', + 'bfd': 'enable', + 'default_information_metric': '6', + 'default_information_metric_type': '1', + 'default_information_originate': 'enable', + 'default_information_route_map': 'test_value_9', + 'default_metric': '10', + 'log_neighbour_changes': 'enable', + 'router_id': 'test_value_12', + 'spf_timers': 'test_value_13', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_ospf6.fortios_router(input_data, fos_instance) + + expected_data = { + 'abr-type': 'cisco', + 'auto-cost-ref-bandwidth': '4', + 'bfd': 'enable', + 'default-information-metric': '6', + 'default-information-metric-type': '1', + 'default-information-originate': 'enable', + 'default-information-route-map': 'test_value_9', + 'default-metric': '10', + 'log-neighbour-changes': 'enable', + 'router-id': 'test_value_12', + 'spf-timers': 'test_value_13', + + } + + set_method_mock.assert_called_with('router', 'ospf6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_ospf6_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_ospf6': { + 'abr_type': 'cisco', + 'auto_cost_ref_bandwidth': '4', + 'bfd': 'enable', + 'default_information_metric': '6', + 'default_information_metric_type': '1', + 'default_information_originate': 'enable', + 'default_information_route_map': 'test_value_9', + 'default_metric': '10', + 'log_neighbour_changes': 'enable', + 'router_id': 'test_value_12', + 'spf_timers': 'test_value_13', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_ospf6.fortios_router(input_data, fos_instance) + + expected_data = { + 'abr-type': 'cisco', + 'auto-cost-ref-bandwidth': '4', + 'bfd': 'enable', + 'default-information-metric': '6', + 'default-information-metric-type': '1', + 'default-information-originate': 'enable', + 'default-information-route-map': 'test_value_9', + 'default-metric': '10', + 'log-neighbour-changes': 'enable', + 'router-id': 'test_value_12', + 'spf-timers': 'test_value_13', + + } + + set_method_mock.assert_called_with('router', 'ospf6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_ospf6_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_ospf6': { + 'random_attribute_not_valid': 'tag', + 'abr_type': 'cisco', + 'auto_cost_ref_bandwidth': '4', + 'bfd': 'enable', + 'default_information_metric': '6', + 'default_information_metric_type': '1', + 'default_information_originate': 'enable', + 'default_information_route_map': 'test_value_9', + 'default_metric': '10', + 'log_neighbour_changes': 'enable', + 'router_id': 'test_value_12', + 'spf_timers': 'test_value_13', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_ospf6.fortios_router(input_data, fos_instance) + + expected_data = { + 'abr-type': 'cisco', + 'auto-cost-ref-bandwidth': '4', + 'bfd': 'enable', + 'default-information-metric': '6', + 'default-information-metric-type': '1', + 'default-information-originate': 'enable', + 'default-information-route-map': 'test_value_9', + 'default-metric': '10', + 'log-neighbour-changes': 'enable', + 'router-id': 'test_value_12', + 'spf-timers': 'test_value_13', + + } + + set_method_mock.assert_called_with('router', 'ospf6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_policy.py b/test/units/modules/network/fortios/test_fortios_router_policy.py new file mode 100644 index 00000000000..98d25361302 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_policy.py @@ -0,0 +1,339 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_policy +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_policy.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_policy_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_policy': { + 'action': 'deny', + 'comments': 'test_value_4', + 'dst_negate': 'enable', + 'end_port': '6', + 'end_source_port': '7', + 'gateway': 'test_value_8', + 'output_device': 'test_value_9', + 'protocol': '10', + 'seq_num': '11', + 'src_negate': 'enable', + 'start_port': '13', + 'start_source_port': '14', + 'status': 'enable', + 'tos': 'test_value_16', + 'tos_mask': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_policy.fortios_router(input_data, fos_instance) + + expected_data = { + 'action': 'deny', + 'comments': 'test_value_4', + 'dst-negate': 'enable', + 'end-port': '6', + 'end-source-port': '7', + 'gateway': 'test_value_8', + 'output-device': 'test_value_9', + 'protocol': '10', + 'seq-num': '11', + 'src-negate': 'enable', + 'start-port': '13', + 'start-source-port': '14', + 'status': 'enable', + 'tos': 'test_value_16', + 'tos-mask': 'test_value_17' + } + + set_method_mock.assert_called_with('router', 'policy', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_policy_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_policy': { + 'action': 'deny', + 'comments': 'test_value_4', + 'dst_negate': 'enable', + 'end_port': '6', + 'end_source_port': '7', + 'gateway': 'test_value_8', + 'output_device': 'test_value_9', + 'protocol': '10', + 'seq_num': '11', + 'src_negate': 'enable', + 'start_port': '13', + 'start_source_port': '14', + 'status': 'enable', + 'tos': 'test_value_16', + 'tos_mask': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_policy.fortios_router(input_data, fos_instance) + + expected_data = { + 'action': 'deny', + 'comments': 'test_value_4', + 'dst-negate': 'enable', + 'end-port': '6', + 'end-source-port': '7', + 'gateway': 'test_value_8', + 'output-device': 'test_value_9', + 'protocol': '10', + 'seq-num': '11', + 'src-negate': 'enable', + 'start-port': '13', + 'start-source-port': '14', + 'status': 'enable', + 'tos': 'test_value_16', + 'tos-mask': 'test_value_17' + } + + set_method_mock.assert_called_with('router', 'policy', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_policy_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'router_policy': { + 'action': 'deny', + 'comments': 'test_value_4', + 'dst_negate': 'enable', + 'end_port': '6', + 'end_source_port': '7', + 'gateway': 'test_value_8', + 'output_device': 'test_value_9', + 'protocol': '10', + 'seq_num': '11', + 'src_negate': 'enable', + 'start_port': '13', + 'start_source_port': '14', + 'status': 'enable', + 'tos': 'test_value_16', + 'tos_mask': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_policy.fortios_router(input_data, fos_instance) + + delete_method_mock.assert_called_with('router', 'policy', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_policy_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'router_policy': { + 'action': 'deny', + 'comments': 'test_value_4', + 'dst_negate': 'enable', + 'end_port': '6', + 'end_source_port': '7', + 'gateway': 'test_value_8', + 'output_device': 'test_value_9', + 'protocol': '10', + 'seq_num': '11', + 'src_negate': 'enable', + 'start_port': '13', + 'start_source_port': '14', + 'status': 'enable', + 'tos': 'test_value_16', + 'tos_mask': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_policy.fortios_router(input_data, fos_instance) + + delete_method_mock.assert_called_with('router', 'policy', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_policy_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_policy': { + 'action': 'deny', + 'comments': 'test_value_4', + 'dst_negate': 'enable', + 'end_port': '6', + 'end_source_port': '7', + 'gateway': 'test_value_8', + 'output_device': 'test_value_9', + 'protocol': '10', + 'seq_num': '11', + 'src_negate': 'enable', + 'start_port': '13', + 'start_source_port': '14', + 'status': 'enable', + 'tos': 'test_value_16', + 'tos_mask': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_policy.fortios_router(input_data, fos_instance) + + expected_data = { + 'action': 'deny', + 'comments': 'test_value_4', + 'dst-negate': 'enable', + 'end-port': '6', + 'end-source-port': '7', + 'gateway': 'test_value_8', + 'output-device': 'test_value_9', + 'protocol': '10', + 'seq-num': '11', + 'src-negate': 'enable', + 'start-port': '13', + 'start-source-port': '14', + 'status': 'enable', + 'tos': 'test_value_16', + 'tos-mask': 'test_value_17' + } + + set_method_mock.assert_called_with('router', 'policy', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_policy_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_policy': { + 'random_attribute_not_valid': 'tag', + 'action': 'deny', + 'comments': 'test_value_4', + 'dst_negate': 'enable', + 'end_port': '6', + 'end_source_port': '7', + 'gateway': 'test_value_8', + 'output_device': 'test_value_9', + 'protocol': '10', + 'seq_num': '11', + 'src_negate': 'enable', + 'start_port': '13', + 'start_source_port': '14', + 'status': 'enable', + 'tos': 'test_value_16', + 'tos_mask': 'test_value_17' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_policy.fortios_router(input_data, fos_instance) + + expected_data = { + 'action': 'deny', + 'comments': 'test_value_4', + 'dst-negate': 'enable', + 'end-port': '6', + 'end-source-port': '7', + 'gateway': 'test_value_8', + 'output-device': 'test_value_9', + 'protocol': '10', + 'seq-num': '11', + 'src-negate': 'enable', + 'start-port': '13', + 'start-source-port': '14', + 'status': 'enable', + 'tos': 'test_value_16', + 'tos-mask': 'test_value_17' + } + + set_method_mock.assert_called_with('router', 'policy', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_policy6.py b/test/units/modules/network/fortios/test_fortios_router_policy6.py new file mode 100644 index 00000000000..44347e833cc --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_policy6.py @@ -0,0 +1,319 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_policy6 +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_policy6.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_policy6_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_policy6': { + 'comments': 'test_value_3', + 'dst': 'test_value_4', + 'end_port': '5', + 'gateway': 'test_value_6', + 'input_device': 'test_value_7', + 'output_device': 'test_value_8', + 'protocol': '9', + 'seq_num': '10', + 'src': 'test_value_11', + 'start_port': '12', + 'status': 'enable', + 'tos': 'test_value_14', + 'tos_mask': 'test_value_15' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_policy6.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'dst': 'test_value_4', + 'end-port': '5', + 'gateway': 'test_value_6', + 'input-device': 'test_value_7', + 'output-device': 'test_value_8', + 'protocol': '9', + 'seq-num': '10', + 'src': 'test_value_11', + 'start-port': '12', + 'status': 'enable', + 'tos': 'test_value_14', + 'tos-mask': 'test_value_15' + } + + set_method_mock.assert_called_with('router', 'policy6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_policy6_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_policy6': { + 'comments': 'test_value_3', + 'dst': 'test_value_4', + 'end_port': '5', + 'gateway': 'test_value_6', + 'input_device': 'test_value_7', + 'output_device': 'test_value_8', + 'protocol': '9', + 'seq_num': '10', + 'src': 'test_value_11', + 'start_port': '12', + 'status': 'enable', + 'tos': 'test_value_14', + 'tos_mask': 'test_value_15' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_policy6.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'dst': 'test_value_4', + 'end-port': '5', + 'gateway': 'test_value_6', + 'input-device': 'test_value_7', + 'output-device': 'test_value_8', + 'protocol': '9', + 'seq-num': '10', + 'src': 'test_value_11', + 'start-port': '12', + 'status': 'enable', + 'tos': 'test_value_14', + 'tos-mask': 'test_value_15' + } + + set_method_mock.assert_called_with('router', 'policy6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_policy6_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'router_policy6': { + 'comments': 'test_value_3', + 'dst': 'test_value_4', + 'end_port': '5', + 'gateway': 'test_value_6', + 'input_device': 'test_value_7', + 'output_device': 'test_value_8', + 'protocol': '9', + 'seq_num': '10', + 'src': 'test_value_11', + 'start_port': '12', + 'status': 'enable', + 'tos': 'test_value_14', + 'tos_mask': 'test_value_15' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_policy6.fortios_router(input_data, fos_instance) + + delete_method_mock.assert_called_with('router', 'policy6', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_policy6_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'router_policy6': { + 'comments': 'test_value_3', + 'dst': 'test_value_4', + 'end_port': '5', + 'gateway': 'test_value_6', + 'input_device': 'test_value_7', + 'output_device': 'test_value_8', + 'protocol': '9', + 'seq_num': '10', + 'src': 'test_value_11', + 'start_port': '12', + 'status': 'enable', + 'tos': 'test_value_14', + 'tos_mask': 'test_value_15' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_policy6.fortios_router(input_data, fos_instance) + + delete_method_mock.assert_called_with('router', 'policy6', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_policy6_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_policy6': { + 'comments': 'test_value_3', + 'dst': 'test_value_4', + 'end_port': '5', + 'gateway': 'test_value_6', + 'input_device': 'test_value_7', + 'output_device': 'test_value_8', + 'protocol': '9', + 'seq_num': '10', + 'src': 'test_value_11', + 'start_port': '12', + 'status': 'enable', + 'tos': 'test_value_14', + 'tos_mask': 'test_value_15' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_policy6.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'dst': 'test_value_4', + 'end-port': '5', + 'gateway': 'test_value_6', + 'input-device': 'test_value_7', + 'output-device': 'test_value_8', + 'protocol': '9', + 'seq-num': '10', + 'src': 'test_value_11', + 'start-port': '12', + 'status': 'enable', + 'tos': 'test_value_14', + 'tos-mask': 'test_value_15' + } + + set_method_mock.assert_called_with('router', 'policy6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_policy6_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_policy6': { + 'random_attribute_not_valid': 'tag', + 'comments': 'test_value_3', + 'dst': 'test_value_4', + 'end_port': '5', + 'gateway': 'test_value_6', + 'input_device': 'test_value_7', + 'output_device': 'test_value_8', + 'protocol': '9', + 'seq_num': '10', + 'src': 'test_value_11', + 'start_port': '12', + 'status': 'enable', + 'tos': 'test_value_14', + 'tos_mask': 'test_value_15' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_policy6.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'dst': 'test_value_4', + 'end-port': '5', + 'gateway': 'test_value_6', + 'input-device': 'test_value_7', + 'output-device': 'test_value_8', + 'protocol': '9', + 'seq-num': '10', + 'src': 'test_value_11', + 'start-port': '12', + 'status': 'enable', + 'tos': 'test_value_14', + 'tos-mask': 'test_value_15' + } + + set_method_mock.assert_called_with('router', 'policy6', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_prefix_list.py b/test/units/modules/network/fortios/test_fortios_router_prefix_list.py new file mode 100644 index 00000000000..7573c3930e1 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_prefix_list.py @@ -0,0 +1,219 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_prefix_list +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_prefix_list.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_prefix_list_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_prefix_list': { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_prefix_list.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + } + + set_method_mock.assert_called_with('router', 'prefix-list', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_prefix_list_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_prefix_list': { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_prefix_list.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + } + + set_method_mock.assert_called_with('router', 'prefix-list', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_prefix_list_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'router_prefix_list': { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_prefix_list.fortios_router(input_data, fos_instance) + + delete_method_mock.assert_called_with('router', 'prefix-list', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_prefix_list_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'router_prefix_list': { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_prefix_list.fortios_router(input_data, fos_instance) + + delete_method_mock.assert_called_with('router', 'prefix-list', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_prefix_list_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_prefix_list': { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_prefix_list.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + } + + set_method_mock.assert_called_with('router', 'prefix-list', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_prefix_list_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_prefix_list': { + 'random_attribute_not_valid': 'tag', + 'comments': 'test_value_3', + 'name': 'default_name_4', + + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_prefix_list.fortios_router(input_data, fos_instance) + + expected_data = { + 'comments': 'test_value_3', + 'name': 'default_name_4', + + } + + set_method_mock.assert_called_with('router', 'prefix-list', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_rip.py b/test/units/modules/network/fortios/test_fortios_router_rip.py new file mode 100644 index 00000000000..995ea75de3b --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_rip.py @@ -0,0 +1,207 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_rip +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_rip.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_rip_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_rip': { + 'default_information_originate': 'enable', + 'default_metric': '4', + 'garbage_timer': '5', + 'max_out_metric': '6', + 'recv_buffer_size': '7', + 'timeout_timer': '8', + 'update_timer': '9', + 'version': '1' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_rip.fortios_router(input_data, fos_instance) + + expected_data = { + 'default-information-originate': 'enable', + 'default-metric': '4', + 'garbage-timer': '5', + 'max-out-metric': '6', + 'recv-buffer-size': '7', + 'timeout-timer': '8', + 'update-timer': '9', + 'version': '1' + } + + set_method_mock.assert_called_with('router', 'rip', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_rip_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_rip': { + 'default_information_originate': 'enable', + 'default_metric': '4', + 'garbage_timer': '5', + 'max_out_metric': '6', + 'recv_buffer_size': '7', + 'timeout_timer': '8', + 'update_timer': '9', + 'version': '1' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_rip.fortios_router(input_data, fos_instance) + + expected_data = { + 'default-information-originate': 'enable', + 'default-metric': '4', + 'garbage-timer': '5', + 'max-out-metric': '6', + 'recv-buffer-size': '7', + 'timeout-timer': '8', + 'update-timer': '9', + 'version': '1' + } + + set_method_mock.assert_called_with('router', 'rip', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_rip_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_rip': { + 'default_information_originate': 'enable', + 'default_metric': '4', + 'garbage_timer': '5', + 'max_out_metric': '6', + 'recv_buffer_size': '7', + 'timeout_timer': '8', + 'update_timer': '9', + 'version': '1' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_rip.fortios_router(input_data, fos_instance) + + expected_data = { + 'default-information-originate': 'enable', + 'default-metric': '4', + 'garbage-timer': '5', + 'max-out-metric': '6', + 'recv-buffer-size': '7', + 'timeout-timer': '8', + 'update-timer': '9', + 'version': '1' + } + + set_method_mock.assert_called_with('router', 'rip', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_rip_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_rip': { + 'random_attribute_not_valid': 'tag', + 'default_information_originate': 'enable', + 'default_metric': '4', + 'garbage_timer': '5', + 'max_out_metric': '6', + 'recv_buffer_size': '7', + 'timeout_timer': '8', + 'update_timer': '9', + 'version': '1' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_rip.fortios_router(input_data, fos_instance) + + expected_data = { + 'default-information-originate': 'enable', + 'default-metric': '4', + 'garbage-timer': '5', + 'max-out-metric': '6', + 'recv-buffer-size': '7', + 'timeout-timer': '8', + 'update-timer': '9', + 'version': '1' + } + + set_method_mock.assert_called_with('router', 'rip', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_setting.py b/test/units/modules/network/fortios/test_fortios_router_setting.py new file mode 100644 index 00000000000..1c8be420cbc --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_setting.py @@ -0,0 +1,159 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_setting +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_setting.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_setting_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_setting': { + 'hostname': 'myhostname3', + 'show_filter': 'test_value_4' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_setting.fortios_router(input_data, fos_instance) + + expected_data = { + 'hostname': 'myhostname3', + 'show-filter': 'test_value_4' + } + + set_method_mock.assert_called_with('router', 'setting', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_setting_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_setting': { + 'hostname': 'myhostname3', + 'show_filter': 'test_value_4' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_setting.fortios_router(input_data, fos_instance) + + expected_data = { + 'hostname': 'myhostname3', + 'show-filter': 'test_value_4' + } + + set_method_mock.assert_called_with('router', 'setting', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_setting_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_setting': { + 'hostname': 'myhostname3', + 'show_filter': 'test_value_4' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_setting.fortios_router(input_data, fos_instance) + + expected_data = { + 'hostname': 'myhostname3', + 'show-filter': 'test_value_4' + } + + set_method_mock.assert_called_with('router', 'setting', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_setting_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_setting': { + 'random_attribute_not_valid': 'tag', + 'hostname': 'myhostname3', + 'show_filter': 'test_value_4' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_setting.fortios_router(input_data, fos_instance) + + expected_data = { + 'hostname': 'myhostname3', + 'show-filter': 'test_value_4' + } + + set_method_mock.assert_called_with('router', 'setting', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 diff --git a/test/units/modules/network/fortios/test_fortios_router_static.py b/test/units/modules/network/fortios/test_fortios_router_static.py new file mode 100644 index 00000000000..91805014967 --- /dev/null +++ b/test/units/modules/network/fortios/test_fortios_router_static.py @@ -0,0 +1,379 @@ +# Copyright 2019 Fortinet, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import json +import pytest +from mock import ANY +from ansible.module_utils.network.fortios.fortios import FortiOSHandler + +try: + from ansible.modules.network.fortios import fortios_router_static +except ImportError: + pytest.skip("Could not load required modules for testing", allow_module_level=True) + + +@pytest.fixture(autouse=True) +def connection_mock(mocker): + connection_class_mock = mocker.patch('ansible.modules.network.fortios.fortios_router_static.Connection') + return connection_class_mock + + +fos_instance = FortiOSHandler(connection_mock) + + +def test_router_static_creation(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_static': { + 'bfd': 'enable', + 'blackhole': 'enable', + 'comment': 'Optional comments.', + 'device': 'test_value_6', + 'distance': '7', + 'dst': 'test_value_8', + 'dstaddr': 'test_value_9', + 'dynamic_gateway': 'enable', + 'gateway': 'test_value_11', + 'internet_service': '12', + 'internet_service_custom': 'test_value_13', + 'link_monitor_exempt': 'enable', + 'priority': '15', + 'seq_num': '16', + 'src': 'test_value_17', + 'status': 'enable', + 'virtual_wan_link': 'enable', + 'vrf': '20', + 'weight': '21' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_static.fortios_router(input_data, fos_instance) + + expected_data = { + 'bfd': 'enable', + 'blackhole': 'enable', + 'comment': 'Optional comments.', + 'device': 'test_value_6', + 'distance': '7', + 'dst': 'test_value_8', + 'dstaddr': 'test_value_9', + 'dynamic-gateway': 'enable', + 'gateway': 'test_value_11', + 'internet-service': '12', + 'internet-service-custom': 'test_value_13', + 'link-monitor-exempt': 'enable', + 'priority': '15', + 'seq-num': '16', + 'src': 'test_value_17', + 'status': 'enable', + 'virtual-wan-link': 'enable', + 'vrf': '20', + 'weight': '21' + } + + set_method_mock.assert_called_with('router', 'static', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_static_creation_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_static': { + 'bfd': 'enable', + 'blackhole': 'enable', + 'comment': 'Optional comments.', + 'device': 'test_value_6', + 'distance': '7', + 'dst': 'test_value_8', + 'dstaddr': 'test_value_9', + 'dynamic_gateway': 'enable', + 'gateway': 'test_value_11', + 'internet_service': '12', + 'internet_service_custom': 'test_value_13', + 'link_monitor_exempt': 'enable', + 'priority': '15', + 'seq_num': '16', + 'src': 'test_value_17', + 'status': 'enable', + 'virtual_wan_link': 'enable', + 'vrf': '20', + 'weight': '21' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_static.fortios_router(input_data, fos_instance) + + expected_data = { + 'bfd': 'enable', + 'blackhole': 'enable', + 'comment': 'Optional comments.', + 'device': 'test_value_6', + 'distance': '7', + 'dst': 'test_value_8', + 'dstaddr': 'test_value_9', + 'dynamic-gateway': 'enable', + 'gateway': 'test_value_11', + 'internet-service': '12', + 'internet-service-custom': 'test_value_13', + 'link-monitor-exempt': 'enable', + 'priority': '15', + 'seq-num': '16', + 'src': 'test_value_17', + 'status': 'enable', + 'virtual-wan-link': 'enable', + 'vrf': '20', + 'weight': '21' + } + + set_method_mock.assert_called_with('router', 'static', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_static_removal(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'router_static': { + 'bfd': 'enable', + 'blackhole': 'enable', + 'comment': 'Optional comments.', + 'device': 'test_value_6', + 'distance': '7', + 'dst': 'test_value_8', + 'dstaddr': 'test_value_9', + 'dynamic_gateway': 'enable', + 'gateway': 'test_value_11', + 'internet_service': '12', + 'internet_service_custom': 'test_value_13', + 'link_monitor_exempt': 'enable', + 'priority': '15', + 'seq_num': '16', + 'src': 'test_value_17', + 'status': 'enable', + 'virtual_wan_link': 'enable', + 'vrf': '20', + 'weight': '21' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_static.fortios_router(input_data, fos_instance) + + delete_method_mock.assert_called_with('router', 'static', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200 + + +def test_router_static_deletion_fails(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + delete_method_result = {'status': 'error', 'http_method': 'POST', 'http_status': 500} + delete_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.delete', return_value=delete_method_result) + + input_data = { + 'username': 'admin', + 'state': 'absent', + 'router_static': { + 'bfd': 'enable', + 'blackhole': 'enable', + 'comment': 'Optional comments.', + 'device': 'test_value_6', + 'distance': '7', + 'dst': 'test_value_8', + 'dstaddr': 'test_value_9', + 'dynamic_gateway': 'enable', + 'gateway': 'test_value_11', + 'internet_service': '12', + 'internet_service_custom': 'test_value_13', + 'link_monitor_exempt': 'enable', + 'priority': '15', + 'seq_num': '16', + 'src': 'test_value_17', + 'status': 'enable', + 'virtual_wan_link': 'enable', + 'vrf': '20', + 'weight': '21' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_static.fortios_router(input_data, fos_instance) + + delete_method_mock.assert_called_with('router', 'static', mkey=ANY, vdom='root') + schema_method_mock.assert_not_called() + assert is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 500 + + +def test_router_static_idempotent(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'error', 'http_method': 'DELETE', 'http_status': 404} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_static': { + 'bfd': 'enable', + 'blackhole': 'enable', + 'comment': 'Optional comments.', + 'device': 'test_value_6', + 'distance': '7', + 'dst': 'test_value_8', + 'dstaddr': 'test_value_9', + 'dynamic_gateway': 'enable', + 'gateway': 'test_value_11', + 'internet_service': '12', + 'internet_service_custom': 'test_value_13', + 'link_monitor_exempt': 'enable', + 'priority': '15', + 'seq_num': '16', + 'src': 'test_value_17', + 'status': 'enable', + 'virtual_wan_link': 'enable', + 'vrf': '20', + 'weight': '21' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_static.fortios_router(input_data, fos_instance) + + expected_data = { + 'bfd': 'enable', + 'blackhole': 'enable', + 'comment': 'Optional comments.', + 'device': 'test_value_6', + 'distance': '7', + 'dst': 'test_value_8', + 'dstaddr': 'test_value_9', + 'dynamic-gateway': 'enable', + 'gateway': 'test_value_11', + 'internet-service': '12', + 'internet-service-custom': 'test_value_13', + 'link-monitor-exempt': 'enable', + 'priority': '15', + 'seq-num': '16', + 'src': 'test_value_17', + 'status': 'enable', + 'virtual-wan-link': 'enable', + 'vrf': '20', + 'weight': '21' + } + + set_method_mock.assert_called_with('router', 'static', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert not changed + assert response['status'] == 'error' + assert response['http_status'] == 404 + + +def test_router_static_filter_foreign_attributes(mocker): + schema_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.schema') + + set_method_result = {'status': 'success', 'http_method': 'POST', 'http_status': 200} + set_method_mock = mocker.patch('ansible.module_utils.network.fortios.fortios.FortiOSHandler.set', return_value=set_method_result) + + input_data = { + 'username': 'admin', + 'state': 'present', + 'router_static': { + 'random_attribute_not_valid': 'tag', + 'bfd': 'enable', + 'blackhole': 'enable', + 'comment': 'Optional comments.', + 'device': 'test_value_6', + 'distance': '7', + 'dst': 'test_value_8', + 'dstaddr': 'test_value_9', + 'dynamic_gateway': 'enable', + 'gateway': 'test_value_11', + 'internet_service': '12', + 'internet_service_custom': 'test_value_13', + 'link_monitor_exempt': 'enable', + 'priority': '15', + 'seq_num': '16', + 'src': 'test_value_17', + 'status': 'enable', + 'virtual_wan_link': 'enable', + 'vrf': '20', + 'weight': '21' + }, + 'vdom': 'root'} + + is_error, changed, response = fortios_router_static.fortios_router(input_data, fos_instance) + + expected_data = { + 'bfd': 'enable', + 'blackhole': 'enable', + 'comment': 'Optional comments.', + 'device': 'test_value_6', + 'distance': '7', + 'dst': 'test_value_8', + 'dstaddr': 'test_value_9', + 'dynamic-gateway': 'enable', + 'gateway': 'test_value_11', + 'internet-service': '12', + 'internet-service-custom': 'test_value_13', + 'link-monitor-exempt': 'enable', + 'priority': '15', + 'seq-num': '16', + 'src': 'test_value_17', + 'status': 'enable', + 'virtual-wan-link': 'enable', + 'vrf': '20', + 'weight': '21' + } + + set_method_mock.assert_called_with('router', 'static', data=expected_data, vdom='root') + schema_method_mock.assert_not_called() + assert not is_error + assert changed + assert response['status'] == 'success' + assert response['http_status'] == 200