FortiOS modules for 2.9 - 8 (#61308)

* FortiOS modules for 2.9 - 8

* Add missing files

* Fix trailing spaces
pull/61322/head
Miguel Angel Muñoz González 5 years ago committed by Nilashish Chakraborty
parent f3f30c146b
commit 69fcd771e0

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_report_layout module: fortios_report_layout
short_description: Report layout configuration in Fortinet's FortiOS and FortiGate. short_description: Report layout configuration in Fortinet's FortiOS and FortiGate.
description: 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. user to set and modify report feature and layout category.
Examples include all parameters and values need to be adjusted to datasources before usage. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,50 +41,66 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true 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: report_layout:
description: description:
- Report layout configuration. - Report layout configuration.
default: null default: null
type: dict
suboptions: suboptions:
state: body_item:
description:
- Indicates whether to create or remove the object
choices:
- present
- absent
body-item:
description: description:
- Configure report body item. - Configure report body item.
type: list
suboptions: suboptions:
chart: chart:
description: description:
- Report item chart name. - Report item chart name.
chart-options: type: str
chart_options:
description: description:
- Report chart options. - Report chart options.
type: str
choices: choices:
- include-no-data - include-no-data
- hide-title - hide-title
@ -95,21 +108,27 @@ options:
column: column:
description: description:
- Report section column number. - Report section column number.
type: int
content: content:
description: description:
- Report item text content. - Report item text content.
type: str
description: description:
description: description:
- Description. - Description.
drill-down-items: type: str
drill_down_items:
description: description:
- Control how drill down charts are shown. - Control how drill down charts are shown.
drill-down-types: type: str
drill_down_types:
description: description:
- Control whether keys from the parent being combined or not. - Control whether keys from the parent being combined or not.
type: str
hide: hide:
description: description:
- Enable/disable hide item in report. - Enable/disable hide item in report.
type: str
choices: choices:
- enable - enable
- disable - disable
@ -117,29 +136,36 @@ options:
description: description:
- Report item ID. - Report item ID.
required: true required: true
img-src: type: int
img_src:
description: description:
- Report item image file name. - Report item image file name.
type: str
list: list:
description: description:
- Configure report list item. - Configure report list item.
type: list
suboptions: suboptions:
content: content:
description: description:
- List entry content. - List entry content.
type: str
id: id:
description: description:
- List entry ID. - List entry ID.
required: true required: true
list-component: type: int
list_component:
description: description:
- Report item list component. - Report item list component.
type: str
choices: choices:
- bullet - bullet
- numbered - numbered
misc-component: misc_component:
description: description:
- Report item miscellaneous component. - Report item miscellaneous component.
type: str
choices: choices:
- hline - hline
- page-break - page-break
@ -148,38 +174,49 @@ options:
parameters: parameters:
description: description:
- Parameters. - Parameters.
type: list
suboptions: suboptions:
id: id:
description: description:
- ID. - ID.
required: true required: true
type: int
name: name:
description: description:
- Field name that match field of parameters defined in dataset. - Field name that match field of parameters defined in dataset.
type: str
value: value:
description: description:
- Value to replace corresponding field of parameters defined in dataset. - Value to replace corresponding field of parameters defined in dataset.
type: str
style: style:
description: description:
- Report item style. - Report item style.
table-caption-style: type: str
table_caption_style:
description: description:
- Table chart caption style. - Table chart caption style.
table-column-widths: type: str
table_column_widths:
description: description:
- Report item table column widths. - Report item table column widths.
table-even-row-style: type: str
table_even_row_style:
description: description:
- Table chart even row style. - Table chart even row style.
table-head-style: type: str
table_head_style:
description: description:
- Table chart head style. - Table chart head style.
table-odd-row-style: type: str
table_odd_row_style:
description: description:
- Table chart odd row style. - Table chart odd row style.
text-component: type: str
text_component:
description: description:
- Report item text component. - Report item text component.
type: str
choices: choices:
- text - text
- heading1 - heading1
@ -188,29 +225,35 @@ options:
title: title:
description: description:
- Report section title. - Report section title.
top-n: type: str
top_n:
description: description:
- Value of top. - Value of top.
type: int
type: type:
description: description:
- Report item type. - Report item type.
type: str
choices: choices:
- text - text
- image - image
- chart - chart
- misc - misc
cutoff-option: cutoff_option:
description: description:
- Cutoff-option is either run-time or custom. - Cutoff-option is either run-time or custom.
type: str
choices: choices:
- run-time - run-time
- custom - custom
cutoff-time: cutoff_time:
description: description:
- "Custom cutoff time to generate report [hh:mm]." - "Custom cutoff time to generate report [hh:mm]."
type: str
day: day:
description: description:
- Schedule days of week to generate report. - Schedule days of week to generate report.
type: str
choices: choices:
- sunday - sunday
- monday - monday
@ -222,30 +265,37 @@ options:
description: description:
description: description:
- Description. - Description.
email-recipients: type: str
email_recipients:
description: description:
- Email recipients for generated reports. - Email recipients for generated reports.
email-send: type: str
email_send:
description: description:
- Enable/disable sending emails after reports are generated. - Enable/disable sending emails after reports are generated.
type: str
choices: choices:
- enable - enable
- disable - disable
format: format:
description: description:
- Report format. - Report format.
type: str
choices: choices:
- pdf - pdf
max-pdf-report: max_pdf_report:
description: description:
- Maximum number of PDF reports to keep at one time (oldest report is overwritten). - Maximum number of PDF reports to keep at one time (oldest report is overwritten).
type: int
name: name:
description: description:
- Report layout name. - Report layout name.
required: true required: true
type: str
options: options:
description: description:
- Report layout options. - Report layout options.
type: str
choices: choices:
- include-table-of-content - include-table-of-content
- auto-numbering-heading - auto-numbering-heading
@ -255,10 +305,12 @@ options:
page: page:
description: description:
- Configure report page. - Configure report page.
type: dict
suboptions: suboptions:
column-break-before: column_break_before:
description: description:
- Report page auto column break before heading. - Report page auto column break before heading.
type: str
choices: choices:
- heading1 - heading1
- heading2 - heading2
@ -266,78 +318,98 @@ options:
footer: footer:
description: description:
- Configure report page footer. - Configure report page footer.
type: dict
suboptions: suboptions:
footer-item: footer_item:
description: description:
- Configure report footer item. - Configure report footer item.
type: list
suboptions: suboptions:
content: content:
description: description:
- Report item text content. - Report item text content.
type: str
description: description:
description: description:
- Description. - Description.
type: str
id: id:
description: description:
- Report item ID. - Report item ID.
required: true required: true
img-src: type: int
img_src:
description: description:
- Report item image file name. - Report item image file name.
type: str
style: style:
description: description:
- Report item style. - Report item style.
type: str
type: type:
description: description:
- Report item type. - Report item type.
type: str
choices: choices:
- text - text
- image - image
style: style:
description: description:
- Report footer style. - Report footer style.
type: str
header: header:
description: description:
- Configure report page header. - Configure report page header.
type: dict
suboptions: suboptions:
header-item: header_item:
description: description:
- Configure report header item. - Configure report header item.
type: list
suboptions: suboptions:
content: content:
description: description:
- Report item text content. - Report item text content.
type: str
description: description:
description: description:
- Description. - Description.
type: str
id: id:
description: description:
- Report item ID. - Report item ID.
required: true required: true
img-src: type: int
img_src:
description: description:
- Report item image file name. - Report item image file name.
type: str
style: style:
description: description:
- Report item style. - Report item style.
type: str
type: type:
description: description:
- Report item type. - Report item type.
type: str
choices: choices:
- text - text
- image - image
style: style:
description: description:
- Report header style. - Report header style.
type: str
options: options:
description: description:
- Report page options. - Report page options.
type: str
choices: choices:
- header-on-first-page - header-on-first-page
- footer-on-first-page - footer-on-first-page
page-break-before: page_break_before:
description: description:
- Report page auto page break before heading. - Report page auto page break before heading.
type: str
choices: choices:
- heading1 - heading1
- heading2 - heading2
@ -345,28 +417,34 @@ options:
paper: paper:
description: description:
- Report page paper. - Report page paper.
type: str
choices: choices:
- a4 - a4
- letter - letter
schedule-type: schedule_type:
description: description:
- Report schedule type. - Report schedule type.
type: str
choices: choices:
- demand - demand
- daily - daily
- weekly - weekly
style-theme: style_theme:
description: description:
- Report style theme. - Report style theme.
type: str
subtitle: subtitle:
description: description:
- Report subtitle. - Report subtitle.
type: str
time: time:
description: description:
- "Schedule time to generate report [hh:mm]." - "Schedule time to generate report [hh:mm]."
type: str
title: title:
description: description:
- Report title. - Report title.
type: str
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -376,6 +454,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Report layout configuration. - name: Report layout configuration.
fortios_report_layout: fortios_report_layout:
@ -384,78 +463,78 @@ EXAMPLES = '''
password: "{{ password }}" password: "{{ password }}"
vdom: "{{ vdom }}" vdom: "{{ vdom }}"
https: "False" https: "False"
state: "present"
report_layout: report_layout:
state: "present" body_item:
body-item:
- -
chart: "<your_own_value>" chart: "<your_own_value>"
chart-options: "include-no-data" chart_options: "include-no-data"
column: "6" column: "6"
content: "<your_own_value>" content: "<your_own_value>"
description: "<your_own_value>" description: "<your_own_value>"
drill-down-items: "<your_own_value>" drill_down_items: "<your_own_value>"
drill-down-types: "<your_own_value>" drill_down_types: "<your_own_value>"
hide: "enable" hide: "enable"
id: "12" id: "12"
img-src: "<your_own_value>" img_src: "<your_own_value>"
list: list:
- -
content: "<your_own_value>" content: "<your_own_value>"
id: "16" id: "16"
list-component: "bullet" list_component: "bullet"
misc-component: "hline" misc_component: "hline"
parameters: parameters:
- -
id: "20" id: "20"
name: "default_name_21" name: "default_name_21"
value: "<your_own_value>" value: "<your_own_value>"
style: "<your_own_value>" style: "<your_own_value>"
table-caption-style: "<your_own_value>" table_caption_style: "<your_own_value>"
table-column-widths: "<your_own_value>" table_column_widths: "<your_own_value>"
table-even-row-style: "<your_own_value>" table_even_row_style: "<your_own_value>"
table-head-style: "<your_own_value>" table_head_style: "<your_own_value>"
table-odd-row-style: "<your_own_value>" table_odd_row_style: "<your_own_value>"
text-component: "text" text_component: "text"
title: "<your_own_value>" title: "<your_own_value>"
top-n: "31" top_n: "31"
type: "text" type: "text"
cutoff-option: "run-time" cutoff_option: "run-time"
cutoff-time: "<your_own_value>" cutoff_time: "<your_own_value>"
day: "sunday" day: "sunday"
description: "<your_own_value>" description: "<your_own_value>"
email-recipients: "<your_own_value>" email_recipients: "<your_own_value>"
email-send: "enable" email_send: "enable"
format: "pdf" format: "pdf"
max-pdf-report: "40" max_pdf_report: "40"
name: "default_name_41" name: "default_name_41"
options: "include-table-of-content" options: "include-table-of-content"
page: page:
column-break-before: "heading1" column_break_before: "heading1"
footer: footer:
footer-item: footer_item:
- -
content: "<your_own_value>" content: "<your_own_value>"
description: "<your_own_value>" description: "<your_own_value>"
id: "49" id: "49"
img-src: "<your_own_value>" img_src: "<your_own_value>"
style: "<your_own_value>" style: "<your_own_value>"
type: "text" type: "text"
style: "<your_own_value>" style: "<your_own_value>"
header: header:
header-item: header_item:
- -
content: "<your_own_value>" content: "<your_own_value>"
description: "<your_own_value>" description: "<your_own_value>"
id: "58" id: "58"
img-src: "<your_own_value>" img_src: "<your_own_value>"
style: "<your_own_value>" style: "<your_own_value>"
type: "text" type: "text"
style: "<your_own_value>" style: "<your_own_value>"
options: "header-on-first-page" options: "header-on-first-page"
page-break-before: "heading1" page_break_before: "heading1"
paper: "a4" paper: "a4"
schedule-type: "demand" schedule_type: "demand"
style-theme: "<your_own_value>" style_theme: "<your_own_value>"
subtitle: "<your_own_value>" subtitle: "<your_own_value>"
time: "<your_own_value>" time: "<your_own_value>"
title: "<your_own_value>" title: "<your_own_value>"
@ -521,14 +600,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -536,15 +617,15 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_report_layout_data(json): def filter_report_layout_data(json):
option_list = ['body-item', 'cutoff-option', 'cutoff-time', option_list = ['body_item', 'cutoff_option', 'cutoff_time',
'day', 'description', 'email-recipients', 'day', 'description', 'email_recipients',
'email-send', 'format', 'max-pdf-report', 'email_send', 'format', 'max_pdf_report',
'name', 'options', 'page', 'name', 'options', 'page',
'schedule-type', 'style-theme', 'subtitle', 'schedule_type', 'style_theme', 'subtitle',
'time', 'title'] 'time', 'title']
dictionary = {} dictionary = {}
@ -555,83 +636,88 @@ def filter_report_layout_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
def report_layout(data, fos): def report_layout(data, fos):
vdom = data['vdom'] vdom = data['vdom']
state = data['state']
report_layout_data = data['report_layout'] report_layout_data = data['report_layout']
flattened_data = flatten_multilists_attributes(report_layout_data) filtered_data = underscore_to_hyphen(filter_report_layout_data(report_layout_data))
filtered_data = filter_report_layout_data(flattened_data)
if report_layout_data['state'] == "present": if state == "present":
return fos.set('report', return fos.set('report',
'layout', 'layout',
data=filtered_data, data=filtered_data,
vdom=vdom) vdom=vdom)
elif report_layout_data['state'] == "absent": elif state == "absent":
return fos.delete('report', return fos.delete('report',
'layout', 'layout',
mkey=filtered_data['name'], mkey=filtered_data['name'],
vdom=vdom) 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): def fortios_report(data, fos):
login(data)
if data['report_layout']: if data['report_layout']:
resp = report_layout(data, fos) resp = report_layout(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "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": { "report_layout": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"state": {"required": True, "type": "str", "body_item": {"required": False, "type": "list",
"choices": ["present", "absent"]},
"body-item": {"required": False, "type": "list",
"options": { "options": {
"chart": {"required": False, "type": "str"}, "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"]}, "choices": ["include-no-data", "hide-title", "show-caption"]},
"column": {"required": False, "type": "int"}, "column": {"required": False, "type": "int"},
"content": {"required": False, "type": "str"}, "content": {"required": False, "type": "str"},
"description": {"required": False, "type": "str"}, "description": {"required": False, "type": "str"},
"drill-down-items": {"required": False, "type": "str"}, "drill_down_items": {"required": False, "type": "str"},
"drill-down-types": {"required": False, "type": "str"}, "drill_down_types": {"required": False, "type": "str"},
"hide": {"required": False, "type": "str", "hide": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"id": {"required": True, "type": "int"}, "id": {"required": True, "type": "int"},
"img-src": {"required": False, "type": "str"}, "img_src": {"required": False, "type": "str"},
"list": {"required": False, "type": "list", "list": {"required": False, "type": "list",
"options": { "options": {
"content": {"required": False, "type": "str"}, "content": {"required": False, "type": "str"},
"id": {"required": True, "type": "int"} "id": {"required": True, "type": "int"}
}}, }},
"list-component": {"required": False, "type": "str", "list_component": {"required": False, "type": "str",
"choices": ["bullet", "numbered"]}, "choices": ["bullet", "numbered"]},
"misc-component": {"required": False, "type": "str", "misc_component": {"required": False, "type": "str",
"choices": ["hline", "page-break", "column-break", "choices": ["hline", "page-break", "column-break",
"section-start"]}, "section-start"]},
"parameters": {"required": False, "type": "list", "parameters": {"required": False, "type": "list",
@ -641,50 +727,50 @@ def main():
"value": {"required": False, "type": "str"} "value": {"required": False, "type": "str"}
}}, }},
"style": {"required": False, "type": "str"}, "style": {"required": False, "type": "str"},
"table-caption-style": {"required": False, "type": "str"}, "table_caption_style": {"required": False, "type": "str"},
"table-column-widths": {"required": False, "type": "str"}, "table_column_widths": {"required": False, "type": "str"},
"table-even-row-style": {"required": False, "type": "str"}, "table_even_row_style": {"required": False, "type": "str"},
"table-head-style": {"required": False, "type": "str"}, "table_head_style": {"required": False, "type": "str"},
"table-odd-row-style": {"required": False, "type": "str"}, "table_odd_row_style": {"required": False, "type": "str"},
"text-component": {"required": False, "type": "str", "text_component": {"required": False, "type": "str",
"choices": ["text", "heading1", "heading2", "choices": ["text", "heading1", "heading2",
"heading3"]}, "heading3"]},
"title": {"required": False, "type": "str"}, "title": {"required": False, "type": "str"},
"top-n": {"required": False, "type": "int"}, "top_n": {"required": False, "type": "int"},
"type": {"required": False, "type": "str", "type": {"required": False, "type": "str",
"choices": ["text", "image", "chart", "choices": ["text", "image", "chart",
"misc"]} "misc"]}
}}, }},
"cutoff-option": {"required": False, "type": "str", "cutoff_option": {"required": False, "type": "str",
"choices": ["run-time", "custom"]}, "choices": ["run-time", "custom"]},
"cutoff-time": {"required": False, "type": "str"}, "cutoff_time": {"required": False, "type": "str"},
"day": {"required": False, "type": "str", "day": {"required": False, "type": "str",
"choices": ["sunday", "monday", "tuesday", "choices": ["sunday", "monday", "tuesday",
"wednesday", "thursday", "friday", "wednesday", "thursday", "friday",
"saturday"]}, "saturday"]},
"description": {"required": False, "type": "str"}, "description": {"required": False, "type": "str"},
"email-recipients": {"required": False, "type": "str"}, "email_recipients": {"required": False, "type": "str"},
"email-send": {"required": False, "type": "str", "email_send": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"format": {"required": False, "type": "str", "format": {"required": False, "type": "str",
"choices": ["pdf"]}, "choices": ["pdf"]},
"max-pdf-report": {"required": False, "type": "int"}, "max_pdf_report": {"required": False, "type": "int"},
"name": {"required": True, "type": "str"}, "name": {"required": True, "type": "str"},
"options": {"required": False, "type": "str", "options": {"required": False, "type": "str",
"choices": ["include-table-of-content", "auto-numbering-heading", "view-chart-as-heading", "choices": ["include-table-of-content", "auto-numbering-heading", "view-chart-as-heading",
"show-html-navbar-before-heading", "dummy-option"]}, "show-html-navbar-before-heading", "dummy-option"]},
"page": {"required": False, "type": "dict", "page": {"required": False, "type": "dict",
"options": { "options": {
"column-break-before": {"required": False, "type": "str", "column_break_before": {"required": False, "type": "str",
"choices": ["heading1", "heading2", "heading3"]}, "choices": ["heading1", "heading2", "heading3"]},
"footer": {"required": False, "type": "dict", "footer": {"required": False, "type": "dict",
"options": { "options": {
"footer-item": {"required": False, "type": "list", "footer_item": {"required": False, "type": "list",
"options": { "options": {
"content": {"required": False, "type": "str"}, "content": {"required": False, "type": "str"},
"description": {"required": False, "type": "str"}, "description": {"required": False, "type": "str"},
"id": {"required": True, "type": "int"}, "id": {"required": True, "type": "int"},
"img-src": {"required": False, "type": "str"}, "img_src": {"required": False, "type": "str"},
"style": {"required": False, "type": "str"}, "style": {"required": False, "type": "str"},
"type": {"required": False, "type": "str", "type": {"required": False, "type": "str",
"choices": ["text", "image"]} "choices": ["text", "image"]}
@ -693,12 +779,12 @@ def main():
}}, }},
"header": {"required": False, "type": "dict", "header": {"required": False, "type": "dict",
"options": { "options": {
"header-item": {"required": False, "type": "list", "header_item": {"required": False, "type": "list",
"options": { "options": {
"content": {"required": False, "type": "str"}, "content": {"required": False, "type": "str"},
"description": {"required": False, "type": "str"}, "description": {"required": False, "type": "str"},
"id": {"required": True, "type": "int"}, "id": {"required": True, "type": "int"},
"img-src": {"required": False, "type": "str"}, "img_src": {"required": False, "type": "str"},
"style": {"required": False, "type": "str"}, "style": {"required": False, "type": "str"},
"type": {"required": False, "type": "str", "type": {"required": False, "type": "str",
"choices": ["text", "image"]} "choices": ["text", "image"]}
@ -707,14 +793,14 @@ def main():
}}, }},
"options": {"required": False, "type": "str", "options": {"required": False, "type": "str",
"choices": ["header-on-first-page", "footer-on-first-page"]}, "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"]}, "choices": ["heading1", "heading2", "heading3"]},
"paper": {"required": False, "type": "str", "paper": {"required": False, "type": "str",
"choices": ["a4", "letter"]} "choices": ["a4", "letter"]}
}}, }},
"schedule-type": {"required": False, "type": "str", "schedule_type": {"required": False, "type": "str",
"choices": ["demand", "daily", "weekly"]}, "choices": ["demand", "daily", "weekly"]},
"style-theme": {"required": False, "type": "str"}, "style_theme": {"required": False, "type": "str"},
"subtitle": {"required": False, "type": "str"}, "subtitle": {"required": False, "type": "str"},
"time": {"required": False, "type": "str"}, "time": {"required": False, "type": "str"},
"title": {"required": False, "type": "str"} "title": {"required": False, "type": "str"}
@ -725,15 +811,31 @@ def main():
module = AnsibleModule(argument_spec=fields, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_report_setting module: fortios_report_setting
short_description: Report setting configuration in Fortinet's FortiOS and FortiGate. short_description: Report setting configuration in Fortinet's FortiOS and FortiGate.
description: 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. user to set and modify report feature and setting category.
Examples include all parameters and values need to be adjusted to datasources before usage. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,59 +41,74 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true default: true
version_added: 2.9
report_setting: report_setting:
description: description:
- Report setting configuration. - Report setting configuration.
default: null default: null
type: dict
suboptions: suboptions:
fortiview: fortiview:
description: description:
- Enable/disable historical FortiView. - Enable/disable historical FortiView.
type: str
choices: choices:
- enable - enable
- disable - disable
pdf-report: pdf_report:
description: description:
- Enable/disable PDF report. - Enable/disable PDF report.
type: str
choices: choices:
- enable - enable
- disable - disable
report-source: report_source:
description: description:
- Report log source. - Report log source.
type: str
choices: choices:
- forward-traffic - forward-traffic
- sniffer-traffic - sniffer-traffic
- local-deny-traffic - local-deny-traffic
top-n: top_n:
description: description:
- Number of items to populate (100 - 4000). - Number of items to populate (100 - 4000).
web-browsing-threshold: type: int
web_browsing_threshold:
description: description:
- Web browsing time calculation threshold (3 - 15 min). - Web browsing time calculation threshold (3 - 15 min).
type: int
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -106,6 +118,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Report setting configuration. - name: Report setting configuration.
fortios_report_setting: fortios_report_setting:
@ -116,10 +129,10 @@ EXAMPLES = '''
https: "False" https: "False"
report_setting: report_setting:
fortiview: "enable" fortiview: "enable"
pdf-report: "enable" pdf_report: "enable"
report-source: "forward-traffic" report_source: "forward-traffic"
top-n: "6" top_n: "6"
web-browsing-threshold: "7" web_browsing_threshold: "7"
''' '''
RETURN = ''' RETURN = '''
@ -182,14 +195,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -197,12 +212,12 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_report_setting_data(json): def filter_report_setting_data(json):
option_list = ['fortiview', 'pdf-report', 'report-source', option_list = ['fortiview', 'pdf_report', 'report_source',
'top-n', 'web-browsing-threshold'] 'top_n', 'web_browsing_threshold']
dictionary = {} dictionary = {}
for attribute in option_list: for attribute in option_list:
@ -212,17 +227,15 @@ def filter_report_setting_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
@ -230,42 +243,48 @@ def flatten_multilists_attributes(data):
def report_setting(data, fos): def report_setting(data, fos):
vdom = data['vdom'] vdom = data['vdom']
report_setting_data = data['report_setting'] report_setting_data = data['report_setting']
flattened_data = flatten_multilists_attributes(report_setting_data) filtered_data = underscore_to_hyphen(filter_report_setting_data(report_setting_data))
filtered_data = filter_report_setting_data(flattened_data)
return fos.set('report', return fos.set('report',
'setting', 'setting',
data=filtered_data, data=filtered_data,
vdom=vdom) 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): def fortios_report(data, fos):
login(data)
if data['report_setting']: if data['report_setting']:
resp = report_setting(data, fos) resp = report_setting(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "https": {"required": False, "type": "bool", "default": True},
"ssl_verify": {"required": False, "type": "bool", "default": True},
"report_setting": { "report_setting": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"fortiview": {"required": False, "type": "str", "fortiview": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"pdf-report": {"required": False, "type": "str", "pdf_report": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"report-source": {"required": False, "type": "str", "report_source": {"required": False, "type": "str",
"choices": ["forward-traffic", "sniffer-traffic", "local-deny-traffic"]}, "choices": ["forward-traffic", "sniffer-traffic", "local-deny-traffic"]},
"top-n": {"required": False, "type": "int"}, "top_n": {"required": False, "type": "int"},
"web-browsing-threshold": {"required": False, "type": "int"} "web_browsing_threshold": {"required": False, "type": "int"}
} }
} }
@ -273,15 +292,31 @@ def main():
module = AnsibleModule(argument_spec=fields, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_report_style module: fortios_report_style
short_description: Report style configuration in Fortinet's FortiOS and FortiGate. short_description: Report style configuration in Fortinet's FortiOS and FortiGate.
description: 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. user to set and modify report feature and style category.
Examples include all parameters and values need to be adjusted to datasources before usage. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,124 +41,158 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true 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: report_style:
description: description:
- Report style configuration. - Report style configuration.
default: null default: null
type: dict
suboptions: suboptions:
state:
description:
- Indicates whether to create or remove the object
choices:
- present
- absent
align: align:
description: description:
- Alignment. - Alignment.
type: str
choices: choices:
- left - left
- center - center
- right - right
- justify - justify
bg-color: bg_color:
description: description:
- Background color. - Background color.
border-bottom: type: str
border_bottom:
description: description:
- Border bottom. - Border bottom.
border-left: type: str
border_left:
description: description:
- Border left. - Border left.
border-right: type: str
border_right:
description: description:
- Border right. - Border right.
border-top: type: str
border_top:
description: description:
- Border top. - Border top.
column-gap: type: str
column_gap:
description: description:
- Column gap. - Column gap.
column-span: type: str
column_span:
description: description:
- Column span. - Column span.
type: str
choices: choices:
- none - none
- all - all
fg-color: fg_color:
description: description:
- Foreground color. - Foreground color.
font-family: type: str
font_family:
description: description:
- Font family. - Font family.
type: str
choices: choices:
- Verdana - Verdana
- Arial - Arial
- Helvetica - Helvetica
- Courier - Courier
- Times - Times
font-size: font_size:
description: description:
- Font size. - Font size.
font-style: type: str
font_style:
description: description:
- Font style. - Font style.
type: str
choices: choices:
- normal - normal
- italic - italic
font-weight: font_weight:
description: description:
- Font weight. - Font weight.
type: str
choices: choices:
- normal - normal
- bold - bold
height: height:
description: description:
- Height. - Height.
line-height: type: str
line_height:
description: description:
- Text line height. - Text line height.
margin-bottom: type: str
margin_bottom:
description: description:
- Margin bottom. - Margin bottom.
margin-left: type: str
margin_left:
description: description:
- Margin left. - Margin left.
margin-right: type: str
margin_right:
description: description:
- Margin right. - Margin right.
margin-top: type: str
margin_top:
description: description:
- Margin top. - Margin top.
type: str
name: name:
description: description:
- Report style name. - Report style name.
required: true required: true
type: str
options: options:
description: description:
- Report style options. - Report style options.
type: str
choices: choices:
- font - font
- text - text
@ -172,21 +203,26 @@ options:
- border - border
- padding - padding
- column - column
padding-bottom: padding_bottom:
description: description:
- Padding bottom. - Padding bottom.
padding-left: type: str
padding_left:
description: description:
- Padding left. - Padding left.
padding-right: type: str
padding_right:
description: description:
- Padding right. - Padding right.
padding-top: type: str
padding_top:
description: description:
- Padding top. - Padding top.
type: str
width: width:
description: description:
- Width. - Width.
type: str
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -196,6 +232,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Report style configuration. - name: Report style configuration.
fortios_report_style: fortios_report_style:
@ -204,33 +241,33 @@ EXAMPLES = '''
password: "{{ password }}" password: "{{ password }}"
vdom: "{{ vdom }}" vdom: "{{ vdom }}"
https: "False" https: "False"
state: "present"
report_style: report_style:
state: "present"
align: "left" align: "left"
bg-color: "<your_own_value>" bg_color: "<your_own_value>"
border-bottom: "<your_own_value>" border_bottom: "<your_own_value>"
border-left: "<your_own_value>" border_left: "<your_own_value>"
border-right: "<your_own_value>" border_right: "<your_own_value>"
border-top: "<your_own_value>" border_top: "<your_own_value>"
column-gap: "<your_own_value>" column_gap: "<your_own_value>"
column-span: "none" column_span: "none"
fg-color: "<your_own_value>" fg_color: "<your_own_value>"
font-family: "Verdana" font_family: "Verdana"
font-size: "<your_own_value>" font_size: "<your_own_value>"
font-style: "normal" font_style: "normal"
font-weight: "normal" font_weight: "normal"
height: "<your_own_value>" height: "<your_own_value>"
line-height: "<your_own_value>" line_height: "<your_own_value>"
margin-bottom: "<your_own_value>" margin_bottom: "<your_own_value>"
margin-left: "<your_own_value>" margin_left: "<your_own_value>"
margin-right: "<your_own_value>" margin_right: "<your_own_value>"
margin-top: "<your_own_value>" margin_top: "<your_own_value>"
name: "default_name_22" name: "default_name_22"
options: "font" options: "font"
padding-bottom: "<your_own_value>" padding_bottom: "<your_own_value>"
padding-left: "<your_own_value>" padding_left: "<your_own_value>"
padding-right: "<your_own_value>" padding_right: "<your_own_value>"
padding-top: "<your_own_value>" padding_top: "<your_own_value>"
width: "<your_own_value>" width: "<your_own_value>"
''' '''
@ -294,14 +331,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -309,19 +348,19 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_report_style_data(json): def filter_report_style_data(json):
option_list = ['align', 'bg-color', 'border-bottom', option_list = ['align', 'bg_color', 'border_bottom',
'border-left', 'border-right', 'border-top', 'border_left', 'border_right', 'border_top',
'column-gap', 'column-span', 'fg-color', 'column_gap', 'column_span', 'fg_color',
'font-family', 'font-size', 'font-style', 'font_family', 'font_size', 'font_style',
'font-weight', 'height', 'line-height', 'font_weight', 'height', 'line_height',
'margin-bottom', 'margin-left', 'margin-right', 'margin_bottom', 'margin_left', 'margin_right',
'margin-top', 'name', 'options', 'margin_top', 'name', 'options',
'padding-bottom', 'padding-left', 'padding-right', 'padding_bottom', 'padding_left', 'padding_right',
'padding-top', 'width'] 'padding_top', 'width']
dictionary = {} dictionary = {}
for attribute in option_list: for attribute in option_list:
@ -331,96 +370,101 @@ def filter_report_style_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
def report_style(data, fos): def report_style(data, fos):
vdom = data['vdom'] vdom = data['vdom']
state = data['state']
report_style_data = data['report_style'] report_style_data = data['report_style']
flattened_data = flatten_multilists_attributes(report_style_data) filtered_data = underscore_to_hyphen(filter_report_style_data(report_style_data))
filtered_data = filter_report_style_data(flattened_data)
if report_style_data['state'] == "present": if state == "present":
return fos.set('report', return fos.set('report',
'style', 'style',
data=filtered_data, data=filtered_data,
vdom=vdom) vdom=vdom)
elif report_style_data['state'] == "absent": elif state == "absent":
return fos.delete('report', return fos.delete('report',
'style', 'style',
mkey=filtered_data['name'], mkey=filtered_data['name'],
vdom=vdom) 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): def fortios_report(data, fos):
login(data)
if data['report_style']: if data['report_style']:
resp = report_style(data, fos) resp = report_style(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "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": { "report_style": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"state": {"required": True, "type": "str",
"choices": ["present", "absent"]},
"align": {"required": False, "type": "str", "align": {"required": False, "type": "str",
"choices": ["left", "center", "right", "choices": ["left", "center", "right",
"justify"]}, "justify"]},
"bg-color": {"required": False, "type": "str"}, "bg_color": {"required": False, "type": "str"},
"border-bottom": {"required": False, "type": "str"}, "border_bottom": {"required": False, "type": "str"},
"border-left": {"required": False, "type": "str"}, "border_left": {"required": False, "type": "str"},
"border-right": {"required": False, "type": "str"}, "border_right": {"required": False, "type": "str"},
"border-top": {"required": False, "type": "str"}, "border_top": {"required": False, "type": "str"},
"column-gap": {"required": False, "type": "str"}, "column_gap": {"required": False, "type": "str"},
"column-span": {"required": False, "type": "str", "column_span": {"required": False, "type": "str",
"choices": ["none", "all"]}, "choices": ["none", "all"]},
"fg-color": {"required": False, "type": "str"}, "fg_color": {"required": False, "type": "str"},
"font-family": {"required": False, "type": "str", "font_family": {"required": False, "type": "str",
"choices": ["Verdana", "Arial", "Helvetica", "choices": ["Verdana", "Arial", "Helvetica",
"Courier", "Times"]}, "Courier", "Times"]},
"font-size": {"required": False, "type": "str"}, "font_size": {"required": False, "type": "str"},
"font-style": {"required": False, "type": "str", "font_style": {"required": False, "type": "str",
"choices": ["normal", "italic"]}, "choices": ["normal", "italic"]},
"font-weight": {"required": False, "type": "str", "font_weight": {"required": False, "type": "str",
"choices": ["normal", "bold"]}, "choices": ["normal", "bold"]},
"height": {"required": False, "type": "str"}, "height": {"required": False, "type": "str"},
"line-height": {"required": False, "type": "str"}, "line_height": {"required": False, "type": "str"},
"margin-bottom": {"required": False, "type": "str"}, "margin_bottom": {"required": False, "type": "str"},
"margin-left": {"required": False, "type": "str"}, "margin_left": {"required": False, "type": "str"},
"margin-right": {"required": False, "type": "str"}, "margin_right": {"required": False, "type": "str"},
"margin-top": {"required": False, "type": "str"}, "margin_top": {"required": False, "type": "str"},
"name": {"required": True, "type": "str"}, "name": {"required": True, "type": "str"},
"options": {"required": False, "type": "str", "options": {"required": False, "type": "str",
"choices": ["font", "text", "color", "choices": ["font", "text", "color",
"align", "size", "margin", "align", "size", "margin",
"border", "padding", "column"]}, "border", "padding", "column"]},
"padding-bottom": {"required": False, "type": "str"}, "padding_bottom": {"required": False, "type": "str"},
"padding-left": {"required": False, "type": "str"}, "padding_left": {"required": False, "type": "str"},
"padding-right": {"required": False, "type": "str"}, "padding_right": {"required": False, "type": "str"},
"padding-top": {"required": False, "type": "str"}, "padding_top": {"required": False, "type": "str"},
"width": {"required": False, "type": "str"} "width": {"required": False, "type": "str"}
} }
@ -429,15 +473,31 @@ def main():
module = AnsibleModule(argument_spec=fields, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_report_theme module: fortios_report_theme
short_description: Report themes configuratio in Fortinet's FortiOS and FortiGate. short_description: Report themes configuratio in Fortinet's FortiOS and FortiGate.
description: 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. user to set and modify report feature and theme category.
Examples include all parameters and values need to be adjusted to datasources before usage. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,138 +41,181 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true 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: report_theme:
description: description:
- Report themes configuration - Report themes configuration
default: null default: null
type: dict
suboptions: suboptions:
state: bullet_list_style:
description:
- Indicates whether to create or remove the object
choices:
- present
- absent
bullet-list-style:
description: description:
- Bullet list style. - Bullet list style.
column-count: type: str
column_count:
description: description:
- Report page column count. - Report page column count.
type: str
choices: choices:
- 1 - 1
- 2 - 2
- 3 - 3
default-html-style: default_html_style:
description: description:
- Default HTML report style. - Default HTML report style.
default-pdf-style: type: str
default_pdf_style:
description: description:
- Default PDF report style. - Default PDF report style.
graph-chart-style: type: str
graph_chart_style:
description: description:
- Graph chart style. - Graph chart style.
heading1-style: type: str
heading1_style:
description: description:
- Report heading style. - Report heading style.
heading2-style: type: str
heading2_style:
description: description:
- Report heading style. - Report heading style.
heading3-style: type: str
heading3_style:
description: description:
- Report heading style. - Report heading style.
heading4-style: type: str
heading4_style:
description: description:
- Report heading style. - Report heading style.
hline-style: type: str
hline_style:
description: description:
- Horizontal line style. - Horizontal line style.
image-style: type: str
image_style:
description: description:
- Image style. - Image style.
type: str
name: name:
description: description:
- Report theme name. - Report theme name.
required: true required: true
normal-text-style: type: str
normal_text_style:
description: description:
- Normal text style. - Normal text style.
numbered-list-style: type: str
numbered_list_style:
description: description:
- Numbered list style. - Numbered list style.
page-footer-style: type: str
page_footer_style:
description: description:
- Report page footer style. - Report page footer style.
page-header-style: type: str
page_header_style:
description: description:
- Report page header style. - Report page header style.
page-orient: type: str
page_orient:
description: description:
- Report page orientation. - Report page orientation.
type: str
choices: choices:
- portrait - portrait
- landscape - landscape
page-style: page_style:
description: description:
- Report page style. - Report page style.
report-subtitle-style: type: str
report_subtitle_style:
description: description:
- Report subtitle style. - Report subtitle style.
report-title-style: type: str
report_title_style:
description: description:
- Report title style. - Report title style.
table-chart-caption-style: type: str
table_chart_caption_style:
description: description:
- Table chart caption style. - Table chart caption style.
table-chart-even-row-style: type: str
table_chart_even_row_style:
description: description:
- Table chart even row style. - Table chart even row style.
table-chart-head-style: type: str
table_chart_head_style:
description: description:
- Table chart head row style. - Table chart head row style.
table-chart-odd-row-style: type: str
table_chart_odd_row_style:
description: description:
- Table chart odd row style. - Table chart odd row style.
table-chart-style: type: str
table_chart_style:
description: description:
- Table chart style. - Table chart style.
toc-heading1-style: type: str
toc_heading1_style:
description: description:
- Table of contents heading style. - Table of contents heading style.
toc-heading2-style: type: str
toc_heading2_style:
description: description:
- Table of contents heading style. - Table of contents heading style.
toc-heading3-style: type: str
toc_heading3_style:
description: description:
- Table of contents heading style. - Table of contents heading style.
toc-heading4-style: type: str
toc_heading4_style:
description: description:
- Table of contents heading style. - Table of contents heading style.
toc-title-style: type: str
toc_title_style:
description: description:
- Table of contents title style. - Table of contents title style.
type: str
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -185,6 +225,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Report themes configuration - name: Report themes configuration
fortios_report_theme: fortios_report_theme:
@ -193,38 +234,38 @@ EXAMPLES = '''
password: "{{ password }}" password: "{{ password }}"
vdom: "{{ vdom }}" vdom: "{{ vdom }}"
https: "False" https: "False"
state: "present"
report_theme: report_theme:
state: "present" bullet_list_style: "<your_own_value>"
bullet-list-style: "<your_own_value>" column_count: "1"
column-count: "1" default_html_style: "<your_own_value>"
default-html-style: "<your_own_value>" default_pdf_style: "<your_own_value>"
default-pdf-style: "<your_own_value>" graph_chart_style: "<your_own_value>"
graph-chart-style: "<your_own_value>" heading1_style: "<your_own_value>"
heading1-style: "<your_own_value>" heading2_style: "<your_own_value>"
heading2-style: "<your_own_value>" heading3_style: "<your_own_value>"
heading3-style: "<your_own_value>" heading4_style: "<your_own_value>"
heading4-style: "<your_own_value>" hline_style: "<your_own_value>"
hline-style: "<your_own_value>" image_style: "<your_own_value>"
image-style: "<your_own_value>"
name: "default_name_14" name: "default_name_14"
normal-text-style: "<your_own_value>" normal_text_style: "<your_own_value>"
numbered-list-style: "<your_own_value>" numbered_list_style: "<your_own_value>"
page-footer-style: "<your_own_value>" page_footer_style: "<your_own_value>"
page-header-style: "<your_own_value>" page_header_style: "<your_own_value>"
page-orient: "portrait" page_orient: "portrait"
page-style: "<your_own_value>" page_style: "<your_own_value>"
report-subtitle-style: "<your_own_value>" report_subtitle_style: "<your_own_value>"
report-title-style: "<your_own_value>" report_title_style: "<your_own_value>"
table-chart-caption-style: "<your_own_value>" table_chart_caption_style: "<your_own_value>"
table-chart-even-row-style: "<your_own_value>" table_chart_even_row_style: "<your_own_value>"
table-chart-head-style: "<your_own_value>" table_chart_head_style: "<your_own_value>"
table-chart-odd-row-style: "<your_own_value>" table_chart_odd_row_style: "<your_own_value>"
table-chart-style: "<your_own_value>" table_chart_style: "<your_own_value>"
toc-heading1-style: "<your_own_value>" toc_heading1_style: "<your_own_value>"
toc-heading2-style: "<your_own_value>" toc_heading2_style: "<your_own_value>"
toc-heading3-style: "<your_own_value>" toc_heading3_style: "<your_own_value>"
toc-heading4-style: "<your_own_value>" toc_heading4_style: "<your_own_value>"
toc-title-style: "<your_own_value>" toc_title_style: "<your_own_value>"
''' '''
RETURN = ''' RETURN = '''
@ -287,14 +328,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -302,20 +345,20 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_report_theme_data(json): def filter_report_theme_data(json):
option_list = ['bullet-list-style', 'column-count', 'default-html-style', option_list = ['bullet_list_style', 'column_count', 'default_html_style',
'default-pdf-style', 'graph-chart-style', 'heading1-style', 'default_pdf_style', 'graph_chart_style', 'heading1_style',
'heading2-style', 'heading3-style', 'heading4-style', 'heading2_style', 'heading3_style', 'heading4_style',
'hline-style', 'image-style', 'name', 'hline_style', 'image_style', 'name',
'normal-text-style', 'numbered-list-style', 'page-footer-style', 'normal_text_style', 'numbered_list_style', 'page_footer_style',
'page-header-style', 'page-orient', 'page-style', 'page_header_style', 'page_orient', 'page_style',
'report-subtitle-style', 'report-title-style', 'table-chart-caption-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_even_row_style', 'table_chart_head_style', 'table_chart_odd_row_style',
'table-chart-style', 'toc-heading1-style', 'toc-heading2-style', 'table_chart_style', 'toc_heading1_style', 'toc_heading2_style',
'toc-heading3-style', 'toc-heading4-style', 'toc-title-style'] 'toc_heading3_style', 'toc_heading4_style', 'toc_title_style']
dictionary = {} dictionary = {}
for attribute in option_list: for attribute in option_list:
@ -325,93 +368,98 @@ def filter_report_theme_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
def report_theme(data, fos): def report_theme(data, fos):
vdom = data['vdom'] vdom = data['vdom']
state = data['state']
report_theme_data = data['report_theme'] report_theme_data = data['report_theme']
flattened_data = flatten_multilists_attributes(report_theme_data) filtered_data = underscore_to_hyphen(filter_report_theme_data(report_theme_data))
filtered_data = filter_report_theme_data(flattened_data)
if report_theme_data['state'] == "present": if state == "present":
return fos.set('report', return fos.set('report',
'theme', 'theme',
data=filtered_data, data=filtered_data,
vdom=vdom) vdom=vdom)
elif report_theme_data['state'] == "absent": elif state == "absent":
return fos.delete('report', return fos.delete('report',
'theme', 'theme',
mkey=filtered_data['name'], mkey=filtered_data['name'],
vdom=vdom) 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): def fortios_report(data, fos):
login(data)
if data['report_theme']: if data['report_theme']:
resp = report_theme(data, fos) resp = report_theme(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "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": { "report_theme": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"state": {"required": True, "type": "str", "bullet_list_style": {"required": False, "type": "str"},
"choices": ["present", "absent"]}, "column_count": {"required": False, "type": "str",
"bullet-list-style": {"required": False, "type": "str"},
"column-count": {"required": False, "type": "str",
"choices": ["1", "2", "3"]}, "choices": ["1", "2", "3"]},
"default-html-style": {"required": False, "type": "str"}, "default_html_style": {"required": False, "type": "str"},
"default-pdf-style": {"required": False, "type": "str"}, "default_pdf_style": {"required": False, "type": "str"},
"graph-chart-style": {"required": False, "type": "str"}, "graph_chart_style": {"required": False, "type": "str"},
"heading1-style": {"required": False, "type": "str"}, "heading1_style": {"required": False, "type": "str"},
"heading2-style": {"required": False, "type": "str"}, "heading2_style": {"required": False, "type": "str"},
"heading3-style": {"required": False, "type": "str"}, "heading3_style": {"required": False, "type": "str"},
"heading4-style": {"required": False, "type": "str"}, "heading4_style": {"required": False, "type": "str"},
"hline-style": {"required": False, "type": "str"}, "hline_style": {"required": False, "type": "str"},
"image-style": {"required": False, "type": "str"}, "image_style": {"required": False, "type": "str"},
"name": {"required": True, "type": "str"}, "name": {"required": True, "type": "str"},
"normal-text-style": {"required": False, "type": "str"}, "normal_text_style": {"required": False, "type": "str"},
"numbered-list-style": {"required": False, "type": "str"}, "numbered_list_style": {"required": False, "type": "str"},
"page-footer-style": {"required": False, "type": "str"}, "page_footer_style": {"required": False, "type": "str"},
"page-header-style": {"required": False, "type": "str"}, "page_header_style": {"required": False, "type": "str"},
"page-orient": {"required": False, "type": "str", "page_orient": {"required": False, "type": "str",
"choices": ["portrait", "landscape"]}, "choices": ["portrait", "landscape"]},
"page-style": {"required": False, "type": "str"}, "page_style": {"required": False, "type": "str"},
"report-subtitle-style": {"required": False, "type": "str"}, "report_subtitle_style": {"required": False, "type": "str"},
"report-title-style": {"required": False, "type": "str"}, "report_title_style": {"required": False, "type": "str"},
"table-chart-caption-style": {"required": False, "type": "str"}, "table_chart_caption_style": {"required": False, "type": "str"},
"table-chart-even-row-style": {"required": False, "type": "str"}, "table_chart_even_row_style": {"required": False, "type": "str"},
"table-chart-head-style": {"required": False, "type": "str"}, "table_chart_head_style": {"required": False, "type": "str"},
"table-chart-odd-row-style": {"required": False, "type": "str"}, "table_chart_odd_row_style": {"required": False, "type": "str"},
"table-chart-style": {"required": False, "type": "str"}, "table_chart_style": {"required": False, "type": "str"},
"toc-heading1-style": {"required": False, "type": "str"}, "toc_heading1_style": {"required": False, "type": "str"},
"toc-heading2-style": {"required": False, "type": "str"}, "toc_heading2_style": {"required": False, "type": "str"},
"toc-heading3-style": {"required": False, "type": "str"}, "toc_heading3_style": {"required": False, "type": "str"},
"toc-heading4-style": {"required": False, "type": "str"}, "toc_heading4_style": {"required": False, "type": "str"},
"toc-title-style": {"required": False, "type": "str"} "toc_title_style": {"required": False, "type": "str"}
} }
} }
@ -419,15 +467,31 @@ def main():
module = AnsibleModule(argument_spec=fields, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_router_access_list module: fortios_router_access_list
short_description: Configure access lists in Fortinet's FortiOS and FortiGate. short_description: Configure access lists in Fortinet's FortiOS and FortiGate.
description: 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. 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. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,76 +41,98 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true 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: router_access_list:
description: description:
- Configure access lists. - Configure access lists.
default: null default: null
type: dict
suboptions: suboptions:
state:
description:
- Indicates whether to create or remove the object
choices:
- present
- absent
comments: comments:
description: description:
- Comment. - Comment.
type: str
name: name:
description: description:
- Name. - Name.
required: true required: true
type: str
rule: rule:
description: description:
- Rule. - Rule.
type: list
suboptions: suboptions:
action: action:
description: description:
- Permit or deny this IP address and netmask prefix. - Permit or deny this IP address and netmask prefix.
type: str
choices: choices:
- permit - permit
- deny - deny
exact-match: exact_match:
description: description:
- Enable/disable exact match. - Enable/disable exact match.
type: str
choices: choices:
- enable - enable
- disable - disable
flags: flags:
description: description:
- Flags. - Flags.
type: int
id: id:
description: description:
- Rule ID. - Rule ID.
required: true required: true
type: int
prefix: prefix:
description: description:
- IPv4 prefix to define regular filter criteria, such as "any" or subnets. - IPv4 prefix to define regular filter criteria, such as "any" or subnets.
type: str
wildcard: wildcard:
description: description:
- Wildcard to define Cisco-style wildcard filter criteria. - Wildcard to define Cisco-style wildcard filter criteria.
type: str
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -123,6 +142,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Configure access lists. - name: Configure access lists.
fortios_router_access_list: fortios_router_access_list:
@ -131,14 +151,14 @@ EXAMPLES = '''
password: "{{ password }}" password: "{{ password }}"
vdom: "{{ vdom }}" vdom: "{{ vdom }}"
https: "False" https: "False"
state: "present"
router_access_list: router_access_list:
state: "present"
comments: "<your_own_value>" comments: "<your_own_value>"
name: "default_name_4" name: "default_name_4"
rule: rule:
- -
action: "permit" action: "permit"
exact-match: "enable" exact_match: "enable"
flags: "8" flags: "8"
id: "9" id: "9"
prefix: "<your_own_value>" prefix: "<your_own_value>"
@ -205,14 +225,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -220,7 +242,7 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_router_access_list_data(json): def filter_router_access_list_data(json):
@ -234,68 +256,73 @@ def filter_router_access_list_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
def router_access_list(data, fos): def router_access_list(data, fos):
vdom = data['vdom'] vdom = data['vdom']
state = data['state']
router_access_list_data = data['router_access_list'] router_access_list_data = data['router_access_list']
flattened_data = flatten_multilists_attributes(router_access_list_data) filtered_data = underscore_to_hyphen(filter_router_access_list_data(router_access_list_data))
filtered_data = filter_router_access_list_data(flattened_data)
if router_access_list_data['state'] == "present": if state == "present":
return fos.set('router', return fos.set('router',
'access-list', 'access-list',
data=filtered_data, data=filtered_data,
vdom=vdom) vdom=vdom)
elif router_access_list_data['state'] == "absent": elif state == "absent":
return fos.delete('router', return fos.delete('router',
'access-list', 'access-list',
mkey=filtered_data['name'], mkey=filtered_data['name'],
vdom=vdom) 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): def fortios_router(data, fos):
login(data)
if data['router_access_list']: if data['router_access_list']:
resp = router_access_list(data, fos) resp = router_access_list(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "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": { "router_access_list": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"state": {"required": True, "type": "str",
"choices": ["present", "absent"]},
"comments": {"required": False, "type": "str"}, "comments": {"required": False, "type": "str"},
"name": {"required": True, "type": "str"}, "name": {"required": True, "type": "str"},
"rule": {"required": False, "type": "list", "rule": {"required": False, "type": "list",
"options": { "options": {
"action": {"required": False, "type": "str", "action": {"required": False, "type": "str",
"choices": ["permit", "deny"]}, "choices": ["permit", "deny"]},
"exact-match": {"required": False, "type": "str", "exact_match": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"flags": {"required": False, "type": "int"}, "flags": {"required": False, "type": "int"},
"id": {"required": True, "type": "int"}, "id": {"required": True, "type": "int"},
@ -309,15 +336,31 @@ def main():
module = AnsibleModule(argument_spec=fields, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_router_auth_path module: fortios_router_auth_path
short_description: Configure authentication based routing in Fortinet's FortiOS and FortiGate. short_description: Configure authentication based routing in Fortinet's FortiOS and FortiGate.
description: 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. 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. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,50 +41,66 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true 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: router_auth_path:
description: description:
- Configure authentication based routing. - Configure authentication based routing.
default: null default: null
type: dict
suboptions: suboptions:
state:
description:
- Indicates whether to create or remove the object
choices:
- present
- absent
device: device:
description: description:
- Outgoing interface. Source system.interface.name. - Outgoing interface. Source system.interface.name.
type: str
gateway: gateway:
description: description:
- Gateway IP address. - Gateway IP address.
type: str
name: name:
description: description:
- Name of the entry. - Name of the entry.
required: true required: true
type: str
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -97,6 +110,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Configure authentication based routing. - name: Configure authentication based routing.
fortios_router_auth_path: fortios_router_auth_path:
@ -105,8 +119,8 @@ EXAMPLES = '''
password: "{{ password }}" password: "{{ password }}"
vdom: "{{ vdom }}" vdom: "{{ vdom }}"
https: "False" https: "False"
state: "present"
router_auth_path: router_auth_path:
state: "present"
device: "<your_own_value> (source system.interface.name)" device: "<your_own_value> (source system.interface.name)"
gateway: "<your_own_value>" gateway: "<your_own_value>"
name: "default_name_5" name: "default_name_5"
@ -172,14 +186,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -187,7 +203,7 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_router_auth_path_data(json): def filter_router_auth_path_data(json):
@ -201,61 +217,66 @@ def filter_router_auth_path_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
def router_auth_path(data, fos): def router_auth_path(data, fos):
vdom = data['vdom'] vdom = data['vdom']
state = data['state']
router_auth_path_data = data['router_auth_path'] router_auth_path_data = data['router_auth_path']
flattened_data = flatten_multilists_attributes(router_auth_path_data) filtered_data = underscore_to_hyphen(filter_router_auth_path_data(router_auth_path_data))
filtered_data = filter_router_auth_path_data(flattened_data)
if router_auth_path_data['state'] == "present": if state == "present":
return fos.set('router', return fos.set('router',
'auth-path', 'auth-path',
data=filtered_data, data=filtered_data,
vdom=vdom) vdom=vdom)
elif router_auth_path_data['state'] == "absent": elif state == "absent":
return fos.delete('router', return fos.delete('router',
'auth-path', 'auth-path',
mkey=filtered_data['name'], mkey=filtered_data['name'],
vdom=vdom) 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): def fortios_router(data, fos):
login(data)
if data['router_auth_path']: if data['router_auth_path']:
resp = router_auth_path(data, fos) resp = router_auth_path(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "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": { "router_auth_path": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"state": {"required": True, "type": "str",
"choices": ["present", "absent"]},
"device": {"required": False, "type": "str"}, "device": {"required": False, "type": "str"},
"gateway": {"required": False, "type": "str"}, "gateway": {"required": False, "type": "str"},
"name": {"required": True, "type": "str"} "name": {"required": True, "type": "str"}
@ -266,15 +287,31 @@ def main():
module = AnsibleModule(argument_spec=fields, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_router_bfd module: fortios_router_bfd
short_description: Configure BFD in Fortinet's FortiOS and FortiGate. short_description: Configure BFD in Fortinet's FortiOS and FortiGate.
description: 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. user to set and modify router feature and bfd category.
Examples include all parameters and values need to be adjusted to datasources before usage. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,45 +41,58 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true default: true
version_added: 2.9
router_bfd: router_bfd:
description: description:
- Configure BFD. - Configure BFD.
default: null default: null
type: dict
suboptions: suboptions:
neighbor: neighbor:
description: description:
- neighbor - neighbor
type: list
suboptions: suboptions:
interface: interface:
description: description:
- Interface name. Source system.interface.name. - Interface name. Source system.interface.name.
type: str
ip: ip:
description: description:
- IPv4 address of the BFD neighbor. - IPv4 address of the BFD neighbor.
required: true required: true
type: str
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -92,6 +102,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Configure BFD. - name: Configure BFD.
fortios_router_bfd: fortios_router_bfd:
@ -167,14 +178,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -182,7 +195,7 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_router_bfd_data(json): def filter_router_bfd_data(json):
@ -196,17 +209,15 @@ def filter_router_bfd_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
@ -214,33 +225,39 @@ def flatten_multilists_attributes(data):
def router_bfd(data, fos): def router_bfd(data, fos):
vdom = data['vdom'] vdom = data['vdom']
router_bfd_data = data['router_bfd'] router_bfd_data = data['router_bfd']
flattened_data = flatten_multilists_attributes(router_bfd_data) filtered_data = underscore_to_hyphen(filter_router_bfd_data(router_bfd_data))
filtered_data = filter_router_bfd_data(flattened_data)
return fos.set('router', return fos.set('router',
'bfd', 'bfd',
data=filtered_data, data=filtered_data,
vdom=vdom) 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): def fortios_router(data, fos):
login(data)
if data['router_bfd']: if data['router_bfd']:
resp = router_bfd(data, fos) resp = router_bfd(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "https": {"required": False, "type": "bool", "default": True},
"ssl_verify": {"required": False, "type": "bool", "default": True},
"router_bfd": { "router_bfd": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"neighbor": {"required": False, "type": "list", "neighbor": {"required": False, "type": "list",
"options": { "options": {
@ -254,15 +271,31 @@ def main():
module = AnsibleModule(argument_spec=fields, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_router_bfd6 module: fortios_router_bfd6
short_description: Configure IPv6 BFD in Fortinet's FortiOS and FortiGate. short_description: Configure IPv6 BFD in Fortinet's FortiOS and FortiGate.
description: 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. user to set and modify router feature and bfd6 category.
Examples include all parameters and values need to be adjusted to datasources before usage. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,45 +41,57 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true default: true
version_added: 2.9
router_bfd6: router_bfd6:
description: description:
- Configure IPv6 BFD. - Configure IPv6 BFD.
default: null default: null
type: dict
suboptions: suboptions:
neighbor: neighbor:
description: description:
- Configure neighbor of IPv6 BFD. - Configure neighbor of IPv6 BFD.
type: list
suboptions: suboptions:
interface: interface:
description: description:
- Interface to the BFD neighbor. Source system.interface.name. - Interface to the BFD neighbor. Source system.interface.name.
ip6-address: type: str
ip6_address:
description: description:
- IPv6 address of the BFD neighbor. - IPv6 address of the BFD neighbor.
required: true type: str
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -92,6 +101,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Configure IPv6 BFD. - name: Configure IPv6 BFD.
fortios_router_bfd6: fortios_router_bfd6:
@ -104,7 +114,7 @@ EXAMPLES = '''
neighbor: neighbor:
- -
interface: "<your_own_value> (source system.interface.name)" interface: "<your_own_value> (source system.interface.name)"
ip6-address: "<your_own_value>" ip6_address: "<your_own_value>"
''' '''
RETURN = ''' RETURN = '''
@ -167,14 +177,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -182,7 +194,7 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_router_bfd6_data(json): def filter_router_bfd6_data(json):
@ -196,17 +208,15 @@ def filter_router_bfd6_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
@ -214,38 +224,44 @@ def flatten_multilists_attributes(data):
def router_bfd6(data, fos): def router_bfd6(data, fos):
vdom = data['vdom'] vdom = data['vdom']
router_bfd6_data = data['router_bfd6'] router_bfd6_data = data['router_bfd6']
flattened_data = flatten_multilists_attributes(router_bfd6_data) filtered_data = underscore_to_hyphen(filter_router_bfd6_data(router_bfd6_data))
filtered_data = filter_router_bfd6_data(flattened_data)
return fos.set('router', return fos.set('router',
'bfd6', 'bfd6',
data=filtered_data, data=filtered_data,
vdom=vdom) 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): def fortios_router(data, fos):
login(data)
if data['router_bfd6']: if data['router_bfd6']:
resp = router_bfd6(data, fos) resp = router_bfd6(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "https": {"required": False, "type": "bool", "default": True},
"ssl_verify": {"required": False, "type": "bool", "default": True},
"router_bfd6": { "router_bfd6": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"neighbor": {"required": False, "type": "list", "neighbor": {"required": False, "type": "list",
"options": { "options": {
"interface": {"required": False, "type": "str"}, "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, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

File diff suppressed because it is too large Load Diff

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_router_multicast module: fortios_router_multicast
short_description: Configure router multicast in Fortinet's FortiOS and FortiGate. short_description: Configure router multicast in Fortinet's FortiOS and FortiGate.
description: 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. user to set and modify router feature and multicast category.
Examples include all parameters and values need to be adjusted to datasources before usage. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,287 +41,361 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true default: true
version_added: 2.9
router_multicast: router_multicast:
description: description:
- Configure router multicast. - Configure router multicast.
default: null default: null
type: dict
suboptions: suboptions:
interface: interface:
description: description:
- PIM interfaces. - PIM interfaces.
type: list
suboptions: suboptions:
bfd: bfd:
description: description:
- Enable/disable Protocol Independent Multicast (PIM) Bidirectional Forwarding Detection (BFD). - Enable/disable Protocol Independent Multicast (PIM) Bidirectional Forwarding Detection (BFD).
type: str
choices: choices:
- enable - enable
- disable - disable
cisco-exclude-genid: cisco_exclude_genid:
description: description:
- Exclude GenID from hello packets (compatibility with old Cisco IOS). - Exclude GenID from hello packets (compatibility with old Cisco IOS).
type: str
choices: choices:
- enable - enable
- disable - disable
dr-priority: dr_priority:
description: description:
- DR election priority. - DR election priority.
hello-holdtime: type: int
hello_holdtime:
description: description:
- Time before old neighbor information expires (0 - 65535 sec, default = 105). - Time before old neighbor information expires (0 - 65535 sec).
hello-interval: type: int
hello_interval:
description: description:
- Interval between sending PIM hello messages (0 - 65535 sec, default = 30). - Interval between sending PIM hello messages (0 - 65535 sec).
type: int
igmp: igmp:
description: description:
- IGMP configuration options. - IGMP configuration options.
type: dict
suboptions: suboptions:
access-group: access_group:
description: description:
- Groups IGMP hosts are allowed to join. Source router.access-list.name. - Groups IGMP hosts are allowed to join. Source router.access-list.name.
immediate-leave-group: type: str
immediate_leave_group:
description: description:
- Groups to drop membership for immediately after receiving IGMPv2 leave. Source router.access-list.name. - 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: description:
- Number of group specific queries before removing group (2 - 7, default = 2). - Number of group specific queries before removing group (2 - 7).
last-member-query-interval: type: int
last_member_query_interval:
description: description:
- Timeout between IGMPv2 leave and removing group (1 - 65535 msec, default = 1000). - Timeout between IGMPv2 leave and removing group (1 - 65535 msec).
query-interval: type: int
query_interval:
description: description:
- Interval between queries to IGMP hosts (1 - 65535 sec, default = 125). - Interval between queries to IGMP hosts (1 - 65535 sec).
query-max-response-time: type: int
query_max_response_time:
description: description:
- Maximum time to wait for a IGMP query response (1 - 25 sec, default = 10). - Maximum time to wait for a IGMP query response (1 - 25 sec).
query-timeout: type: int
query_timeout:
description: description:
- Timeout between queries before becoming querier for network (60 - 900, default = 255). - Timeout between queries before becoming querier for network (60 - 900).
router-alert-check: type: int
router_alert_check:
description: description:
- Enable/disable require IGMP packets contain router alert option. - Enable/disable require IGMP packets contain router alert option.
type: str
choices: choices:
- enable - enable
- disable - disable
version: version:
description: description:
- Maximum version of IGMP to support. - Maximum version of IGMP to support.
type: str
choices: choices:
- 3 - 3
- 2 - 2
- 1 - 1
join-group: join_group:
description: description:
- Join multicast groups. - Join multicast groups.
type: list
suboptions: suboptions:
address: address:
description: description:
- Multicast group IP address. - Multicast group IP address.
required: true required: true
multicast-flow: type: str
multicast_flow:
description: description:
- Acceptable source for multicast group. Source router.multicast-flow.name. - Acceptable source for multicast group. Source router.multicast-flow.name.
type: str
name: name:
description: description:
- Interface name. Source system.interface.name. - Interface name. Source system.interface.name.
required: true required: true
neighbour-filter: type: str
neighbour_filter:
description: description:
- Routers acknowledged as neighbor routers. Source router.access-list.name. - Routers acknowledged as neighbor routers. Source router.access-list.name.
type: str
passive: passive:
description: description:
- Enable/disable listening to IGMP but not participating in PIM. - Enable/disable listening to IGMP but not participating in PIM.
type: str
choices: choices:
- enable - enable
- disable - disable
pim-mode: pim_mode:
description: description:
- PIM operation mode. - PIM operation mode.
type: str
choices: choices:
- sparse-mode - sparse-mode
- dense-mode - dense-mode
propagation-delay: propagation_delay:
description: description:
- Delay flooding packets on this interface (100 - 5000 msec, default = 500). - Delay flooding packets on this interface (100 - 5000 msec).
rp-candidate: type: int
rp_candidate:
description: description:
- Enable/disable compete to become RP in elections. - Enable/disable compete to become RP in elections.
type: str
choices: choices:
- enable - enable
- disable - disable
rp-candidate-group: rp_candidate_group:
description: description:
- Multicast groups managed by this RP. Source router.access-list.name. - Multicast groups managed by this RP. Source router.access-list.name.
rp-candidate-interval: type: str
rp_candidate_interval:
description: description:
- RP candidate advertisement interval (1 - 16383 sec, default = 60). - RP candidate advertisement interval (1 - 16383 sec).
rp-candidate-priority: type: int
rp_candidate_priority:
description: description:
- Router's priority as RP. - Router's priority as RP.
state-refresh-interval: type: int
state_refresh_interval:
description: description:
- Interval between sending state-refresh packets (1 - 100 sec, default = 60). - Interval between sending state-refresh packets (1 - 100 sec).
static-group: type: int
static_group:
description: description:
- Statically set multicast groups to forward out. Source router.multicast-flow.name. - Statically set multicast groups to forward out. Source router.multicast-flow.name.
ttl-threshold: type: str
ttl_threshold:
description: description:
- Minimum TTL of multicast packets that will be forwarded (applied only to new multicast routes) (1 - 255, default = 1). - Minimum TTL of multicast packets that will be forwarded (applied only to new multicast routes) (1 - 255).
multicast-routing: type: int
multicast_routing:
description: description:
- Enable/disable IP multicast routing. - Enable/disable IP multicast routing.
type: str
choices: choices:
- enable - enable
- disable - disable
pim-sm-global: pim_sm_global:
description: description:
- PIM sparse-mode global settings. - PIM sparse-mode global settings.
type: dict
suboptions: suboptions:
accept-register-list: accept_register_list:
description: description:
- Sources allowed to register packets with this Rendezvous Point (RP). Source router.access-list.name. - Sources allowed to register packets with this Rendezvous Point (RP). Source router.access-list.name.
accept-source-list: type: str
accept_source_list:
description: description:
- Sources allowed to send multicast traffic. Source router.access-list.name. - Sources allowed to send multicast traffic. Source router.access-list.name.
bsr-allow-quick-refresh: type: str
bsr_allow_quick_refresh:
description: description:
- Enable/disable accept BSR quick refresh packets from neighbors. - Enable/disable accept BSR quick refresh packets from neighbors.
type: str
choices: choices:
- enable - enable
- disable - disable
bsr-candidate: bsr_candidate:
description: description:
- Enable/disable allowing this router to become a bootstrap router (BSR). - Enable/disable allowing this router to become a bootstrap router (BSR).
type: str
choices: choices:
- enable - enable
- disable - disable
bsr-hash: bsr_hash:
description: description:
- BSR hash length (0 - 32, default = 10). - BSR hash length (0 - 32).
bsr-interface: type: int
bsr_interface:
description: description:
- Interface to advertise as candidate BSR. Source system.interface.name. - Interface to advertise as candidate BSR. Source system.interface.name.
bsr-priority: type: str
bsr_priority:
description: description:
- BSR priority (0 - 255, default = 0). - BSR priority (0 - 255).
cisco-crp-prefix: type: int
cisco_crp_prefix:
description: description:
- Enable/disable making candidate RP compatible with old Cisco IOS. - Enable/disable making candidate RP compatible with old Cisco IOS.
type: str
choices: choices:
- enable - enable
- disable - disable
cisco-ignore-rp-set-priority: cisco_ignore_rp_set_priority:
description: description:
- Use only hash for RP selection (compatibility with old Cisco IOS). - Use only hash for RP selection (compatibility with old Cisco IOS).
type: str
choices: choices:
- enable - enable
- disable - disable
cisco-register-checksum: cisco_register_checksum:
description: description:
- Checksum entire register packet(for old Cisco IOS compatibility). - Checksum entire register packet(for old Cisco IOS compatibility).
type: str
choices: choices:
- enable - enable
- disable - disable
cisco-register-checksum-group: cisco_register_checksum_group:
description: description:
- Cisco register checksum only these groups. Source router.access-list.name. - Cisco register checksum only these groups. Source router.access-list.name.
join-prune-holdtime: type: str
join_prune_holdtime:
description: description:
- Join/prune holdtime (1 - 65535, default = 210). - Join/prune holdtime (1 - 65535).
message-interval: type: int
message_interval:
description: description:
- Period of time between sending periodic PIM join/prune messages in seconds (1 - 65535, default = 60). - Period of time between sending periodic PIM join/prune messages in seconds (1 - 65535).
null-register-retries: type: int
null_register_retries:
description: description:
- Maximum retries of null register (1 - 20, default = 1). - Maximum retries of null register (1 - 20).
register-rate-limit: type: int
register_rate_limit:
description: description:
- Limit of packets/sec per source registered through this RP (0 - 65535, default = 0 which means unlimited). - Limit of packets/sec per source registered through this RP (0 - 65535).
register-rp-reachability: type: int
register_rp_reachability:
description: description:
- Enable/disable check RP is reachable before registering packets. - Enable/disable check RP is reachable before registering packets.
type: str
choices: choices:
- enable - enable
- disable - disable
register-source: register_source:
description: description:
- Override source address in register packets. - Override source address in register packets.
type: str
choices: choices:
- disable - disable
- interface - interface
- ip-address - ip-address
register-source-interface: register_source_interface:
description: description:
- Override with primary interface address. Source system.interface.name. - Override with primary interface address. Source system.interface.name.
register-source-ip: type: str
register_source_ip:
description: description:
- Override with local IP address. - Override with local IP address.
register-supression: type: str
register_supression:
description: description:
- Period of time to honor register-stop message (1 - 65535 sec, default = 60). - Period of time to honor register-stop message (1 - 65535 sec).
rp-address: type: int
rp_address:
description: description:
- Statically configure RP addresses. - Statically configure RP addresses.
type: list
suboptions: suboptions:
group: group:
description: description:
- Groups to use this RP. Source router.access-list.name. - Groups to use this RP. Source router.access-list.name.
type: str
id: id:
description: description:
- ID. - ID.
required: true required: true
ip-address: type: int
ip_address:
description: description:
- RP router address. - RP router address.
rp-register-keepalive: type: str
rp_register_keepalive:
description: description:
- Timeout for RP receiving data on (S,G) tree (1 - 65535 sec, default = 185). - Timeout for RP receiving data on (S,G) tree (1 - 65535 sec).
spt-threshold: type: int
spt_threshold:
description: description:
- Enable/disable switching to source specific trees. - Enable/disable switching to source specific trees.
type: str
choices: choices:
- enable - enable
- disable - disable
spt-threshold-group: spt_threshold_group:
description: description:
- Groups allowed to switch to source tree. Source router.access-list.name. - Groups allowed to switch to source tree. Source router.access-list.name.
type: str
ssm: ssm:
description: description:
- Enable/disable source specific multicast. - Enable/disable source specific multicast.
type: str
choices: choices:
- enable - enable
- disable - disable
ssm-range: ssm_range:
description: description:
- Groups allowed to source specific multicast. Source router.access-list.name. - Groups allowed to source specific multicast. Source router.access-list.name.
route-limit: type: str
route_limit:
description: description:
- Maximum number of multicast routes. - Maximum number of multicast routes.
route-threshold: type: int
route_threshold:
description: description:
- Generate warnings when the number of multicast routes exceeds this number, must not be greater than route-limit. - Generate warnings when the number of multicast routes exceeds this number, must not be greater than route-limit.
type: int
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -334,6 +405,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Configure router multicast. - name: Configure router multicast.
fortios_router_multicast: fortios_router_multicast:
@ -346,70 +418,70 @@ EXAMPLES = '''
interface: interface:
- -
bfd: "enable" bfd: "enable"
cisco-exclude-genid: "enable" cisco_exclude_genid: "enable"
dr-priority: "6" dr_priority: "6"
hello-holdtime: "7" hello_holdtime: "7"
hello-interval: "8" hello_interval: "8"
igmp: igmp:
access-group: "<your_own_value> (source router.access-list.name)" access_group: "<your_own_value> (source router.access-list.name)"
immediate-leave-group: "<your_own_value> (source router.access-list.name)" immediate_leave_group: "<your_own_value> (source router.access-list.name)"
last-member-query-count: "12" last_member_query_count: "12"
last-member-query-interval: "13" last_member_query_interval: "13"
query-interval: "14" query_interval: "14"
query-max-response-time: "15" query_max_response_time: "15"
query-timeout: "16" query_timeout: "16"
router-alert-check: "enable" router_alert_check: "enable"
version: "3" version: "3"
join-group: join_group:
- -
address: "<your_own_value>" address: "<your_own_value>"
multicast-flow: "<your_own_value> (source router.multicast-flow.name)" multicast_flow: "<your_own_value> (source router.multicast-flow.name)"
name: "default_name_22 (source system.interface.name)" name: "default_name_22 (source system.interface.name)"
neighbour-filter: "<your_own_value> (source router.access-list.name)" neighbour_filter: "<your_own_value> (source router.access-list.name)"
passive: "enable" passive: "enable"
pim-mode: "sparse-mode" pim_mode: "sparse-mode"
propagation-delay: "26" propagation_delay: "26"
rp-candidate: "enable" rp_candidate: "enable"
rp-candidate-group: "<your_own_value> (source router.access-list.name)" rp_candidate_group: "<your_own_value> (source router.access-list.name)"
rp-candidate-interval: "29" rp_candidate_interval: "29"
rp-candidate-priority: "30" rp_candidate_priority: "30"
state-refresh-interval: "31" state_refresh_interval: "31"
static-group: "<your_own_value> (source router.multicast-flow.name)" static_group: "<your_own_value> (source router.multicast-flow.name)"
ttl-threshold: "33" ttl_threshold: "33"
multicast-routing: "enable" multicast_routing: "enable"
pim-sm-global: pim_sm_global:
accept-register-list: "<your_own_value> (source router.access-list.name)" accept_register_list: "<your_own_value> (source router.access-list.name)"
accept-source-list: "<your_own_value> (source router.access-list.name)" accept_source_list: "<your_own_value> (source router.access-list.name)"
bsr-allow-quick-refresh: "enable" bsr_allow_quick_refresh: "enable"
bsr-candidate: "enable" bsr_candidate: "enable"
bsr-hash: "40" bsr_hash: "40"
bsr-interface: "<your_own_value> (source system.interface.name)" bsr_interface: "<your_own_value> (source system.interface.name)"
bsr-priority: "42" bsr_priority: "42"
cisco-crp-prefix: "enable" cisco_crp_prefix: "enable"
cisco-ignore-rp-set-priority: "enable" cisco_ignore_rp_set_priority: "enable"
cisco-register-checksum: "enable" cisco_register_checksum: "enable"
cisco-register-checksum-group: "<your_own_value> (source router.access-list.name)" cisco_register_checksum_group: "<your_own_value> (source router.access-list.name)"
join-prune-holdtime: "47" join_prune_holdtime: "47"
message-interval: "48" message_interval: "48"
null-register-retries: "49" null_register_retries: "49"
register-rate-limit: "50" register_rate_limit: "50"
register-rp-reachability: "enable" register_rp_reachability: "enable"
register-source: "disable" register_source: "disable"
register-source-interface: "<your_own_value> (source system.interface.name)" register_source_interface: "<your_own_value> (source system.interface.name)"
register-source-ip: "<your_own_value>" register_source_ip: "<your_own_value>"
register-supression: "55" register_supression: "55"
rp-address: rp_address:
- -
group: "<your_own_value> (source router.access-list.name)" group: "<your_own_value> (source router.access-list.name)"
id: "58" id: "58"
ip-address: "<your_own_value>" ip_address: "<your_own_value>"
rp-register-keepalive: "60" rp_register_keepalive: "60"
spt-threshold: "enable" spt_threshold: "enable"
spt-threshold-group: "<your_own_value> (source router.access-list.name)" spt_threshold_group: "<your_own_value> (source router.access-list.name)"
ssm: "enable" ssm: "enable"
ssm-range: "<your_own_value> (source router.access-list.name)" ssm_range: "<your_own_value> (source router.access-list.name)"
route-limit: "65" route_limit: "65"
route-threshold: "66" route_threshold: "66"
''' '''
RETURN = ''' RETURN = '''
@ -472,14 +544,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -487,12 +561,12 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_router_multicast_data(json): def filter_router_multicast_data(json):
option_list = ['interface', 'multicast-routing', 'pim-sm-global', option_list = ['interface', 'multicast_routing', 'pim_sm_global',
'route-limit', 'route-threshold'] 'route_limit', 'route_threshold']
dictionary = {} dictionary = {}
for attribute in option_list: for attribute in option_list:
@ -502,17 +576,15 @@ def filter_router_multicast_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
@ -520,125 +592,131 @@ def flatten_multilists_attributes(data):
def router_multicast(data, fos): def router_multicast(data, fos):
vdom = data['vdom'] vdom = data['vdom']
router_multicast_data = data['router_multicast'] router_multicast_data = data['router_multicast']
flattened_data = flatten_multilists_attributes(router_multicast_data) filtered_data = underscore_to_hyphen(filter_router_multicast_data(router_multicast_data))
filtered_data = filter_router_multicast_data(flattened_data)
return fos.set('router', return fos.set('router',
'multicast', 'multicast',
data=filtered_data, data=filtered_data,
vdom=vdom) 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): def fortios_router(data, fos):
login(data)
if data['router_multicast']: if data['router_multicast']:
resp = router_multicast(data, fos) resp = router_multicast(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "https": {"required": False, "type": "bool", "default": True},
"ssl_verify": {"required": False, "type": "bool", "default": True},
"router_multicast": { "router_multicast": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"interface": {"required": False, "type": "list", "interface": {"required": False, "type": "list",
"options": { "options": {
"bfd": {"required": False, "type": "str", "bfd": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"cisco-exclude-genid": {"required": False, "type": "str", "cisco_exclude_genid": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"dr-priority": {"required": False, "type": "int"}, "dr_priority": {"required": False, "type": "int"},
"hello-holdtime": {"required": False, "type": "int"}, "hello_holdtime": {"required": False, "type": "int"},
"hello-interval": {"required": False, "type": "int"}, "hello_interval": {"required": False, "type": "int"},
"igmp": {"required": False, "type": "dict", "igmp": {"required": False, "type": "dict",
"options": { "options": {
"access-group": {"required": False, "type": "str"}, "access_group": {"required": False, "type": "str"},
"immediate-leave-group": {"required": False, "type": "str"}, "immediate_leave_group": {"required": False, "type": "str"},
"last-member-query-count": {"required": False, "type": "int"}, "last_member_query_count": {"required": False, "type": "int"},
"last-member-query-interval": {"required": False, "type": "int"}, "last_member_query_interval": {"required": False, "type": "int"},
"query-interval": {"required": False, "type": "int"}, "query_interval": {"required": False, "type": "int"},
"query-max-response-time": {"required": False, "type": "int"}, "query_max_response_time": {"required": False, "type": "int"},
"query-timeout": {"required": False, "type": "int"}, "query_timeout": {"required": False, "type": "int"},
"router-alert-check": {"required": False, "type": "str", "router_alert_check": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"version": {"required": False, "type": "str", "version": {"required": False, "type": "str",
"choices": ["3", "2", "1"]} "choices": ["3", "2", "1"]}
}}, }},
"join-group": {"required": False, "type": "list", "join_group": {"required": False, "type": "list",
"options": { "options": {
"address": {"required": True, "type": "str"} "address": {"required": True, "type": "str"}
}}, }},
"multicast-flow": {"required": False, "type": "str"}, "multicast_flow": {"required": False, "type": "str"},
"name": {"required": True, "type": "str"}, "name": {"required": True, "type": "str"},
"neighbour-filter": {"required": False, "type": "str"}, "neighbour_filter": {"required": False, "type": "str"},
"passive": {"required": False, "type": "str", "passive": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"pim-mode": {"required": False, "type": "str", "pim_mode": {"required": False, "type": "str",
"choices": ["sparse-mode", "dense-mode"]}, "choices": ["sparse-mode", "dense-mode"]},
"propagation-delay": {"required": False, "type": "int"}, "propagation_delay": {"required": False, "type": "int"},
"rp-candidate": {"required": False, "type": "str", "rp_candidate": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"rp-candidate-group": {"required": False, "type": "str"}, "rp_candidate_group": {"required": False, "type": "str"},
"rp-candidate-interval": {"required": False, "type": "int"}, "rp_candidate_interval": {"required": False, "type": "int"},
"rp-candidate-priority": {"required": False, "type": "int"}, "rp_candidate_priority": {"required": False, "type": "int"},
"state-refresh-interval": {"required": False, "type": "int"}, "state_refresh_interval": {"required": False, "type": "int"},
"static-group": {"required": False, "type": "str"}, "static_group": {"required": False, "type": "str"},
"ttl-threshold": {"required": False, "type": "int"} "ttl_threshold": {"required": False, "type": "int"}
}}, }},
"multicast-routing": {"required": False, "type": "str", "multicast_routing": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"pim-sm-global": {"required": False, "type": "dict", "pim_sm_global": {"required": False, "type": "dict",
"options": { "options": {
"accept-register-list": {"required": False, "type": "str"}, "accept_register_list": {"required": False, "type": "str"},
"accept-source-list": {"required": False, "type": "str"}, "accept_source_list": {"required": False, "type": "str"},
"bsr-allow-quick-refresh": {"required": False, "type": "str", "bsr_allow_quick_refresh": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"bsr-candidate": {"required": False, "type": "str", "bsr_candidate": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"bsr-hash": {"required": False, "type": "int"}, "bsr_hash": {"required": False, "type": "int"},
"bsr-interface": {"required": False, "type": "str"}, "bsr_interface": {"required": False, "type": "str"},
"bsr-priority": {"required": False, "type": "int"}, "bsr_priority": {"required": False, "type": "int"},
"cisco-crp-prefix": {"required": False, "type": "str", "cisco_crp_prefix": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"cisco-ignore-rp-set-priority": {"required": False, "type": "str", "cisco_ignore_rp_set_priority": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"cisco-register-checksum": {"required": False, "type": "str", "cisco_register_checksum": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"cisco-register-checksum-group": {"required": False, "type": "str"}, "cisco_register_checksum_group": {"required": False, "type": "str"},
"join-prune-holdtime": {"required": False, "type": "int"}, "join_prune_holdtime": {"required": False, "type": "int"},
"message-interval": {"required": False, "type": "int"}, "message_interval": {"required": False, "type": "int"},
"null-register-retries": {"required": False, "type": "int"}, "null_register_retries": {"required": False, "type": "int"},
"register-rate-limit": {"required": False, "type": "int"}, "register_rate_limit": {"required": False, "type": "int"},
"register-rp-reachability": {"required": False, "type": "str", "register_rp_reachability": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"register-source": {"required": False, "type": "str", "register_source": {"required": False, "type": "str",
"choices": ["disable", "interface", "ip-address"]}, "choices": ["disable", "interface", "ip-address"]},
"register-source-interface": {"required": False, "type": "str"}, "register_source_interface": {"required": False, "type": "str"},
"register-source-ip": {"required": False, "type": "str"}, "register_source_ip": {"required": False, "type": "str"},
"register-supression": {"required": False, "type": "int"}, "register_supression": {"required": False, "type": "int"},
"rp-address": {"required": False, "type": "list", "rp_address": {"required": False, "type": "list",
"options": { "options": {
"group": {"required": False, "type": "str"}, "group": {"required": False, "type": "str"},
"id": {"required": True, "type": "int"}, "id": {"required": True, "type": "int"},
"ip-address": {"required": False, "type": "str"} "ip_address": {"required": False, "type": "str"}
}}, }},
"rp-register-keepalive": {"required": False, "type": "int"}, "rp_register_keepalive": {"required": False, "type": "int"},
"spt-threshold": {"required": False, "type": "str", "spt_threshold": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"spt-threshold-group": {"required": False, "type": "str"}, "spt_threshold_group": {"required": False, "type": "str"},
"ssm": {"required": False, "type": "str", "ssm": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"ssm-range": {"required": False, "type": "str"} "ssm_range": {"required": False, "type": "str"}
}}, }},
"route-limit": {"required": False, "type": "int"}, "route_limit": {"required": False, "type": "int"},
"route-threshold": {"required": False, "type": "int"} "route_threshold": {"required": False, "type": "int"}
} }
} }
@ -646,15 +724,31 @@ def main():
module = AnsibleModule(argument_spec=fields, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_router_multicast6 module: fortios_router_multicast6
short_description: Configure IPv6 multicast in Fortinet's FortiOS and FortiGate. short_description: Configure IPv6 multicast in Fortinet's FortiOS and FortiGate.
description: 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. user to set and modify router feature and multicast6 category.
Examples include all parameters and values need to be adjusted to datasources before usage. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,78 +41,99 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true default: true
version_added: 2.9
router_multicast6: router_multicast6:
description: description:
- Configure IPv6 multicast. - Configure IPv6 multicast.
default: null default: null
type: dict
suboptions: suboptions:
interface: interface:
description: description:
- Protocol Independent Multicast (PIM) interfaces. - Protocol Independent Multicast (PIM) interfaces.
type: list
suboptions: suboptions:
hello-holdtime: hello_holdtime:
description: description:
- Time before old neighbour information expires (1 - 65535 sec, default = 105). - Time before old neighbour information expires (1 - 65535 sec).
hello-interval: type: int
hello_interval:
description: description:
- Interval between sending PIM hello messages (1 - 65535 sec, default = 30).. - Interval between sending PIM hello messages (1 - 65535 sec)..
type: int
name: name:
description: description:
- Interface name. Source system.interface.name. - Interface name. Source system.interface.name.
required: true required: true
multicast-pmtu: type: str
multicast_pmtu:
description: description:
- Enable/disable PMTU for IPv6 multicast. - Enable/disable PMTU for IPv6 multicast.
type: str
choices: choices:
- enable - enable
- disable - disable
multicast-routing: multicast_routing:
description: description:
- Enable/disable IPv6 multicast routing. - Enable/disable IPv6 multicast routing.
type: str
choices: choices:
- enable - enable
- disable - disable
pim-sm-global: pim_sm_global:
description: description:
- PIM sparse-mode global settings. - PIM sparse-mode global settings.
type: dict
suboptions: suboptions:
register-rate-limit: register_rate_limit:
description: description:
- Limit of packets/sec per source registered through this RP (0 means unlimited). - Limit of packets/sec per source registered through this RP (0 means unlimited).
rp-address: type: int
rp_address:
description: description:
- Statically configured RP addresses. - Statically configured RP addresses.
type: list
suboptions: suboptions:
id: id:
description: description:
- ID of the entry. - ID of the entry.
required: true required: true
ip6-address: type: int
ip6_address:
description: description:
- RP router IPv6 address. - RP router IPv6 address.
type: str
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -125,6 +143,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Configure IPv6 multicast. - name: Configure IPv6 multicast.
fortios_router_multicast6: fortios_router_multicast6:
@ -136,17 +155,17 @@ EXAMPLES = '''
router_multicast6: router_multicast6:
interface: interface:
- -
hello-holdtime: "4" hello_holdtime: "4"
hello-interval: "5" hello_interval: "5"
name: "default_name_6 (source system.interface.name)" name: "default_name_6 (source system.interface.name)"
multicast-pmtu: "enable" multicast_pmtu: "enable"
multicast-routing: "enable" multicast_routing: "enable"
pim-sm-global: pim_sm_global:
register-rate-limit: "10" register_rate_limit: "10"
rp-address: rp_address:
- -
id: "12" id: "12"
ip6-address: "<your_own_value>" ip6_address: "<your_own_value>"
''' '''
RETURN = ''' RETURN = '''
@ -209,14 +228,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -224,12 +245,12 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_router_multicast6_data(json): def filter_router_multicast6_data(json):
option_list = ['interface', 'multicast-pmtu', 'multicast-routing', option_list = ['interface', 'multicast_pmtu', 'multicast_routing',
'pim-sm-global'] 'pim_sm_global']
dictionary = {} dictionary = {}
for attribute in option_list: for attribute in option_list:
@ -239,17 +260,15 @@ def filter_router_multicast6_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
@ -257,51 +276,57 @@ def flatten_multilists_attributes(data):
def router_multicast6(data, fos): def router_multicast6(data, fos):
vdom = data['vdom'] vdom = data['vdom']
router_multicast6_data = data['router_multicast6'] router_multicast6_data = data['router_multicast6']
flattened_data = flatten_multilists_attributes(router_multicast6_data) filtered_data = underscore_to_hyphen(filter_router_multicast6_data(router_multicast6_data))
filtered_data = filter_router_multicast6_data(flattened_data)
return fos.set('router', return fos.set('router',
'multicast6', 'multicast6',
data=filtered_data, data=filtered_data,
vdom=vdom) 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): def fortios_router(data, fos):
login(data)
if data['router_multicast6']: if data['router_multicast6']:
resp = router_multicast6(data, fos) resp = router_multicast6(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "https": {"required": False, "type": "bool", "default": True},
"ssl_verify": {"required": False, "type": "bool", "default": True},
"router_multicast6": { "router_multicast6": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"interface": {"required": False, "type": "list", "interface": {"required": False, "type": "list",
"options": { "options": {
"hello-holdtime": {"required": False, "type": "int"}, "hello_holdtime": {"required": False, "type": "int"},
"hello-interval": {"required": False, "type": "int"}, "hello_interval": {"required": False, "type": "int"},
"name": {"required": True, "type": "str"} "name": {"required": True, "type": "str"}
}}, }},
"multicast-pmtu": {"required": False, "type": "str", "multicast_pmtu": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"multicast-routing": {"required": False, "type": "str", "multicast_routing": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"pim-sm-global": {"required": False, "type": "dict", "pim_sm_global": {"required": False, "type": "dict",
"options": { "options": {
"register-rate-limit": {"required": False, "type": "int"}, "register_rate_limit": {"required": False, "type": "int"},
"rp-address": {"required": False, "type": "list", "rp_address": {"required": False, "type": "list",
"options": { "options": {
"id": {"required": True, "type": "int"}, "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, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_router_multicast_flow module: fortios_router_multicast_flow
short_description: Configure multicast-flow in Fortinet's FortiOS and FortiGate. short_description: Configure multicast-flow in Fortinet's FortiOS and FortiGate.
description: 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. 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. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,61 +41,80 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true 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: router_multicast_flow:
description: description:
- Configure multicast-flow. - Configure multicast-flow.
default: null default: null
type: dict
suboptions: suboptions:
state:
description:
- Indicates whether to create or remove the object
choices:
- present
- absent
comments: comments:
description: description:
- Comment. - Comment.
type: str
flows: flows:
description: description:
- Multicast-flow entries. - Multicast-flow entries.
type: list
suboptions: suboptions:
group-addr: group_addr:
description: description:
- Multicast group IP address. - Multicast group IP address.
type: str
id: id:
description: description:
- Flow ID. - Flow ID.
required: true required: true
source-addr: type: int
source_addr:
description: description:
- Multicast source IP address. - Multicast source IP address.
type: str
name: name:
description: description:
- Name. - Name.
required: true required: true
type: str
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -108,6 +124,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Configure multicast-flow. - name: Configure multicast-flow.
fortios_router_multicast_flow: fortios_router_multicast_flow:
@ -116,14 +133,14 @@ EXAMPLES = '''
password: "{{ password }}" password: "{{ password }}"
vdom: "{{ vdom }}" vdom: "{{ vdom }}"
https: "False" https: "False"
state: "present"
router_multicast_flow: router_multicast_flow:
state: "present"
comments: "<your_own_value>" comments: "<your_own_value>"
flows: flows:
- -
group-addr: "<your_own_value>" group_addr: "<your_own_value>"
id: "6" id: "6"
source-addr: "<your_own_value>" source_addr: "<your_own_value>"
name: "default_name_8" name: "default_name_8"
''' '''
@ -187,14 +204,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -202,7 +221,7 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_router_multicast_flow_data(json): def filter_router_multicast_flow_data(json):
@ -216,67 +235,72 @@ def filter_router_multicast_flow_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
def router_multicast_flow(data, fos): def router_multicast_flow(data, fos):
vdom = data['vdom'] vdom = data['vdom']
state = data['state']
router_multicast_flow_data = data['router_multicast_flow'] router_multicast_flow_data = data['router_multicast_flow']
flattened_data = flatten_multilists_attributes(router_multicast_flow_data) filtered_data = underscore_to_hyphen(filter_router_multicast_flow_data(router_multicast_flow_data))
filtered_data = filter_router_multicast_flow_data(flattened_data)
if router_multicast_flow_data['state'] == "present": if state == "present":
return fos.set('router', return fos.set('router',
'multicast-flow', 'multicast-flow',
data=filtered_data, data=filtered_data,
vdom=vdom) vdom=vdom)
elif router_multicast_flow_data['state'] == "absent": elif state == "absent":
return fos.delete('router', return fos.delete('router',
'multicast-flow', 'multicast-flow',
mkey=filtered_data['name'], mkey=filtered_data['name'],
vdom=vdom) 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): def fortios_router(data, fos):
login(data)
if data['router_multicast_flow']: if data['router_multicast_flow']:
resp = router_multicast_flow(data, fos) resp = router_multicast_flow(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "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": { "router_multicast_flow": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"state": {"required": True, "type": "str",
"choices": ["present", "absent"]},
"comments": {"required": False, "type": "str"}, "comments": {"required": False, "type": "str"},
"flows": {"required": False, "type": "list", "flows": {"required": False, "type": "list",
"options": { "options": {
"group-addr": {"required": False, "type": "str"}, "group_addr": {"required": False, "type": "str"},
"id": {"required": True, "type": "int"}, "id": {"required": True, "type": "int"},
"source-addr": {"required": False, "type": "str"} "source_addr": {"required": False, "type": "str"}
}}, }},
"name": {"required": True, "type": "str"} "name": {"required": True, "type": "str"}
@ -286,15 +310,31 @@ def main():
module = AnsibleModule(argument_spec=fields, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

File diff suppressed because it is too large Load Diff

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_router_ospf6 module: fortios_router_ospf6
short_description: Configure IPv6 OSPF in Fortinet's FortiOS and FortiGate. short_description: Configure IPv6 OSPF in Fortinet's FortiOS and FortiGate.
description: 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. user to set and modify router feature and ospf6 category.
Examples include all parameters and values need to be adjusted to datasources before usage. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,37 +41,48 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true default: true
version_added: 2.9
router_ospf6: router_ospf6:
description: description:
- Configure IPv6 OSPF. - Configure IPv6 OSPF.
default: null default: null
type: dict
suboptions: suboptions:
abr-type: abr_type:
description: description:
- Area border router type. - Area border router type.
type: str
choices: choices:
- cisco - cisco
- ibm - ibm
@ -82,38 +90,46 @@ options:
area: area:
description: description:
- OSPF6 area configuration. - OSPF6 area configuration.
type: list
suboptions: suboptions:
default-cost: default_cost:
description: description:
- Summary default cost of stub or NSSA area. - Summary default cost of stub or NSSA area.
type: int
id: id:
description: description:
- Area entry IP address. - Area entry IP address.
required: true required: true
nssa-default-information-originate: type: str
nssa_default_information_originate:
description: description:
- Enable/disable originate type 7 default into NSSA area. - Enable/disable originate type 7 default into NSSA area.
type: str
choices: choices:
- enable - enable
- disable - disable
nssa-default-information-originate-metric: nssa_default_information_originate_metric:
description: description:
- OSPFv3 default metric. - OSPFv3 default metric.
nssa-default-information-originate-metric-type: type: int
nssa_default_information_originate_metric_type:
description: description:
- OSPFv3 metric type for default routes. - OSPFv3 metric type for default routes.
type: str
choices: choices:
- 1 - 1
- 2 - 2
nssa-redistribution: nssa_redistribution:
description: description:
- Enable/disable redistribute into NSSA area. - Enable/disable redistribute into NSSA area.
type: str
choices: choices:
- enable - enable
- disable - disable
nssa-translator-role: nssa_translator_role:
description: description:
- NSSA translator role type. - NSSA translator role type.
type: str
choices: choices:
- candidate - candidate
- never - never
@ -121,10 +137,12 @@ options:
range: range:
description: description:
- OSPF6 area range configuration. - OSPF6 area range configuration.
type: list
suboptions: suboptions:
advertise: advertise:
description: description:
- Enable/disable advertise status. - Enable/disable advertise status.
type: str
choices: choices:
- disable - disable
- enable - enable
@ -132,92 +150,114 @@ options:
description: description:
- Range entry ID. - Range entry ID.
required: true required: true
type: int
prefix6: prefix6:
description: description:
- IPv6 prefix. - IPv6 prefix.
stub-type: type: str
stub_type:
description: description:
- Stub summary setting. - Stub summary setting.
type: str
choices: choices:
- no-summary - no-summary
- summary - summary
type: type:
description: description:
- Area type setting. - Area type setting.
type: str
choices: choices:
- regular - regular
- nssa - nssa
- stub - stub
virtual-link: virtual_link:
description: description:
- OSPF6 virtual link configuration. - OSPF6 virtual link configuration.
type: list
suboptions: suboptions:
dead-interval: dead_interval:
description: description:
- Dead interval. - Dead interval.
hello-interval: type: int
hello_interval:
description: description:
- Hello interval. - Hello interval.
type: int
name: name:
description: description:
- Virtual link entry name. - Virtual link entry name.
required: true required: true
type: str
peer: peer:
description: description:
- A.B.C.D, peer router ID. - A.B.C.D, peer router ID.
retransmit-interval: type: str
retransmit_interval:
description: description:
- Retransmit interval. - Retransmit interval.
transmit-delay: type: int
transmit_delay:
description: description:
- Transmit delay. - Transmit delay.
auto-cost-ref-bandwidth: type: int
auto_cost_ref_bandwidth:
description: description:
- Reference bandwidth in terms of megabits per second. - Reference bandwidth in terms of megabits per second.
type: int
bfd: bfd:
description: description:
- Enable/disable Bidirectional Forwarding Detection (BFD). - Enable/disable Bidirectional Forwarding Detection (BFD).
type: str
choices: choices:
- enable - enable
- disable - disable
default-information-metric: default_information_metric:
description: description:
- Default information metric. - Default information metric.
default-information-metric-type: type: int
default_information_metric_type:
description: description:
- Default information metric type. - Default information metric type.
type: str
choices: choices:
- 1 - 1
- 2 - 2
default-information-originate: default_information_originate:
description: description:
- Enable/disable generation of default route. - Enable/disable generation of default route.
type: str
choices: choices:
- enable - enable
- always - always
- disable - disable
default-information-route-map: default_information_route_map:
description: description:
- Default information route map. Source router.route-map.name. - Default information route map. Source router.route-map.name.
default-metric: type: str
default_metric:
description: description:
- Default metric of redistribute routes. - Default metric of redistribute routes.
log-neighbour-changes: type: int
log_neighbour_changes:
description: description:
- Enable logging of OSPFv3 neighbour's changes - Enable logging of OSPFv3 neighbour's changes
type: str
choices: choices:
- enable - enable
- disable - disable
ospf6-interface: ospf6_interface:
description: description:
- OSPF6 interface configuration. - OSPF6 interface configuration.
type: list
suboptions: suboptions:
area-id: area_id:
description: description:
- A.B.C.D, in IPv4 address format. - A.B.C.D, in IPv4 address format.
type: str
bfd: bfd:
description: description:
- Enable/disable Bidirectional Forwarding Detection (BFD). - Enable/disable Bidirectional Forwarding Detection (BFD).
type: str
choices: choices:
- global - global
- enable - enable
@ -225,39 +265,61 @@ options:
cost: cost:
description: description:
- Cost of the interface, value range from 0 to 65535, 0 means auto-cost. - Cost of the interface, value range from 0 to 65535, 0 means auto-cost.
dead-interval: type: int
dead_interval:
description: description:
- Dead interval. - Dead interval.
hello-interval: type: int
hello_interval:
description: description:
- Hello interval. - Hello interval.
type: int
interface: interface:
description: description:
- Configuration interface name. Source system.interface.name. - 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: name:
description: description:
- Interface entry name. - Interface entry name.
required: true required: true
type: str
neighbor: neighbor:
description: description:
- OSPFv3 neighbors are used when OSPFv3 runs on non-broadcast media - OSPFv3 neighbors are used when OSPFv3 runs on non-broadcast media
type: list
suboptions: suboptions:
cost: cost:
description: description:
- Cost of the interface, value range from 0 to 65535, 0 means auto-cost. - Cost of the interface, value range from 0 to 65535, 0 means auto-cost.
type: int
ip6: ip6:
description: description:
- IPv6 link local address of the neighbor. - IPv6 link local address of the neighbor.
required: true required: true
poll-interval: type: str
poll_interval:
description: description:
- Poll interval time in seconds. - Poll interval time in seconds.
type: int
priority: priority:
description: description:
- priority - priority
network-type: type: int
network_type:
description: description:
- Network type. - Network type.
type: str
choices: choices:
- broadcast - broadcast
- point-to-point - point-to-point
@ -267,36 +329,45 @@ options:
priority: priority:
description: description:
- priority - priority
retransmit-interval: type: int
retransmit_interval:
description: description:
- Retransmit interval. - Retransmit interval.
type: int
status: status:
description: description:
- Enable/disable OSPF6 routing on this interface. - Enable/disable OSPF6 routing on this interface.
type: str
choices: choices:
- disable - disable
- enable - enable
transmit-delay: transmit_delay:
description: description:
- Transmit delay. - Transmit delay.
passive-interface: type: int
passive_interface:
description: description:
- Passive interface configuration. - Passive interface configuration.
type: list
suboptions: suboptions:
name: name:
description: description:
- Passive interface name. Source system.interface.name. - Passive interface name. Source system.interface.name.
required: true required: true
type: str
redistribute: redistribute:
description: description:
- Redistribute configuration. - Redistribute configuration.
type: list
suboptions: suboptions:
metric: metric:
description: description:
- Redistribute metric setting. - Redistribute metric setting.
metric-type: type: int
metric_type:
description: description:
- Metric type. - Metric type.
type: str
choices: choices:
- 1 - 1
- 2 - 2
@ -304,28 +375,35 @@ options:
description: description:
- Redistribute name. - Redistribute name.
required: true required: true
type: str
routemap: routemap:
description: description:
- Route map name. Source router.route-map.name. - Route map name. Source router.route-map.name.
type: str
status: status:
description: description:
- status - status
type: str
choices: choices:
- enable - enable
- disable - disable
router-id: router_id:
description: description:
- A.B.C.D, in IPv4 address format. - A.B.C.D, in IPv4 address format.
spf-timers: type: str
spf_timers:
description: description:
- SPF calculation frequency. - SPF calculation frequency.
summary-address: type: str
summary_address:
description: description:
- IPv6 address summary configuration. - IPv6 address summary configuration.
type: list
suboptions: suboptions:
advertise: advertise:
description: description:
- Enable/disable advertise status. - Enable/disable advertise status.
type: str
choices: choices:
- disable - disable
- enable - enable
@ -333,12 +411,15 @@ options:
description: description:
- Summary address entry ID. - Summary address entry ID.
required: true required: true
type: int
prefix6: prefix6:
description: description:
- IPv6 prefix. - IPv6 prefix.
type: str
tag: tag:
description: description:
- Tag value. - Tag value.
type: int
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -348,6 +429,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Configure IPv6 OSPF. - name: Configure IPv6 OSPF.
fortios_router_ospf6: fortios_router_ospf6:
@ -357,77 +439,79 @@ EXAMPLES = '''
vdom: "{{ vdom }}" vdom: "{{ vdom }}"
https: "False" https: "False"
router_ospf6: router_ospf6:
abr-type: "cisco" abr_type: "cisco"
area: area:
- -
default-cost: "5" default_cost: "5"
id: "6" id: "6"
nssa-default-information-originate: "enable" nssa_default_information_originate: "enable"
nssa-default-information-originate-metric: "8" nssa_default_information_originate_metric: "8"
nssa-default-information-originate-metric-type: "1" nssa_default_information_originate_metric_type: "1"
nssa-redistribution: "enable" nssa_redistribution: "enable"
nssa-translator-role: "candidate" nssa_translator_role: "candidate"
range: range:
- -
advertise: "disable" advertise: "disable"
id: "14" id: "14"
prefix6: "<your_own_value>" prefix6: "<your_own_value>"
stub-type: "no-summary" stub_type: "no-summary"
type: "regular" type: "regular"
virtual-link: virtual_link:
- -
dead-interval: "19" dead_interval: "19"
hello-interval: "20" hello_interval: "20"
name: "default_name_21" name: "default_name_21"
peer: "<your_own_value>" peer: "<your_own_value>"
retransmit-interval: "23" retransmit_interval: "23"
transmit-delay: "24" transmit_delay: "24"
auto-cost-ref-bandwidth: "25" auto_cost_ref_bandwidth: "25"
bfd: "enable" bfd: "enable"
default-information-metric: "27" default_information_metric: "27"
default-information-metric-type: "1" default_information_metric_type: "1"
default-information-originate: "enable" default_information_originate: "enable"
default-information-route-map: "<your_own_value> (source router.route-map.name)" default_information_route_map: "<your_own_value> (source router.route-map.name)"
default-metric: "31" default_metric: "31"
log-neighbour-changes: "enable" log_neighbour_changes: "enable"
ospf6-interface: ospf6_interface:
- -
area-id: "<your_own_value>" area_id: "<your_own_value>"
bfd: "global" bfd: "global"
cost: "36" cost: "36"
dead-interval: "37" dead_interval: "37"
hello-interval: "38" hello_interval: "38"
interface: "<your_own_value> (source system.interface.name)" interface: "<your_own_value> (source system.interface.name)"
name: "default_name_40" mtu: "40"
mtu_ignore: "enable"
name: "default_name_42"
neighbor: neighbor:
- -
cost: "42" cost: "44"
ip6: "<your_own_value>" ip6: "<your_own_value>"
poll-interval: "44" poll_interval: "46"
priority: "45" priority: "47"
network-type: "broadcast" network_type: "broadcast"
priority: "47" priority: "49"
retransmit-interval: "48" retransmit_interval: "50"
status: "disable" status: "disable"
transmit-delay: "50" transmit_delay: "52"
passive-interface: passive_interface:
- -
name: "default_name_52 (source system.interface.name)" name: "default_name_54 (source system.interface.name)"
redistribute: redistribute:
- -
metric: "54" metric: "56"
metric-type: "1" metric_type: "1"
name: "default_name_56" name: "default_name_58"
routemap: "<your_own_value> (source router.route-map.name)" routemap: "<your_own_value> (source router.route-map.name)"
status: "enable" status: "enable"
router-id: "<your_own_value>" router_id: "<your_own_value>"
spf-timers: "<your_own_value>" spf_timers: "<your_own_value>"
summary-address: summary_address:
- -
advertise: "disable" advertise: "disable"
id: "63" id: "65"
prefix6: "<your_own_value>" prefix6: "<your_own_value>"
tag: "65" tag: "67"
''' '''
RETURN = ''' RETURN = '''
@ -490,14 +574,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -505,16 +591,16 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_router_ospf6_data(json): def filter_router_ospf6_data(json):
option_list = ['abr-type', 'area', 'auto-cost-ref-bandwidth', option_list = ['abr_type', 'area', 'auto_cost_ref_bandwidth',
'bfd', 'default-information-metric', 'default-information-metric-type', 'bfd', 'default_information_metric', 'default_information_metric_type',
'default-information-originate', 'default-information-route-map', 'default-metric', 'default_information_originate', 'default_information_route_map', 'default_metric',
'log-neighbour-changes', 'ospf6-interface', 'passive-interface', 'log_neighbour_changes', 'ospf6_interface', 'passive_interface',
'redistribute', 'router-id', 'spf-timers', 'redistribute', 'router_id', 'spf_timers',
'summary-address'] 'summary_address']
dictionary = {} dictionary = {}
for attribute in option_list: for attribute in option_list:
@ -524,17 +610,15 @@ def filter_router_ospf6_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
@ -542,48 +626,54 @@ def flatten_multilists_attributes(data):
def router_ospf6(data, fos): def router_ospf6(data, fos):
vdom = data['vdom'] vdom = data['vdom']
router_ospf6_data = data['router_ospf6'] router_ospf6_data = data['router_ospf6']
flattened_data = flatten_multilists_attributes(router_ospf6_data) filtered_data = underscore_to_hyphen(filter_router_ospf6_data(router_ospf6_data))
filtered_data = filter_router_ospf6_data(flattened_data)
return fos.set('router', return fos.set('router',
'ospf6', 'ospf6',
data=filtered_data, data=filtered_data,
vdom=vdom) 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): def fortios_router(data, fos):
login(data)
if data['router_ospf6']: if data['router_ospf6']:
resp = router_ospf6(data, fos) resp = router_ospf6(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "https": {"required": False, "type": "bool", "default": True},
"ssl_verify": {"required": False, "type": "bool", "default": True},
"router_ospf6": { "router_ospf6": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"abr-type": {"required": False, "type": "str", "abr_type": {"required": False, "type": "str",
"choices": ["cisco", "ibm", "standard"]}, "choices": ["cisco", "ibm", "standard"]},
"area": {"required": False, "type": "list", "area": {"required": False, "type": "list",
"options": { "options": {
"default-cost": {"required": False, "type": "int"}, "default_cost": {"required": False, "type": "int"},
"id": {"required": True, "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", "disable"]}, "choices": ["enable", "disable"]},
"nssa-default-information-originate-metric": {"required": False, "type": "int"}, "nssa_default_information_originate_metric": {"required": False, "type": "int"},
"nssa-default-information-originate-metric-type": {"required": False, "type": "str", "nssa_default_information_originate_metric_type": {"required": False, "type": "str",
"choices": ["1", "2"]}, "choices": ["1", "2"]},
"nssa-redistribution": {"required": False, "type": "str", "nssa_redistribution": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"nssa-translator-role": {"required": False, "type": "str", "nssa_translator_role": {"required": False, "type": "str",
"choices": ["candidate", "never", "always"]}, "choices": ["candidate", "never", "always"]},
"range": {"required": False, "type": "list", "range": {"required": False, "type": "list",
"options": { "options": {
@ -592,75 +682,78 @@ def main():
"id": {"required": True, "type": "int"}, "id": {"required": True, "type": "int"},
"prefix6": {"required": False, "type": "str"} "prefix6": {"required": False, "type": "str"}
}}, }},
"stub-type": {"required": False, "type": "str", "stub_type": {"required": False, "type": "str",
"choices": ["no-summary", "summary"]}, "choices": ["no-summary", "summary"]},
"type": {"required": False, "type": "str", "type": {"required": False, "type": "str",
"choices": ["regular", "nssa", "stub"]}, "choices": ["regular", "nssa", "stub"]},
"virtual-link": {"required": False, "type": "list", "virtual_link": {"required": False, "type": "list",
"options": { "options": {
"dead-interval": {"required": False, "type": "int"}, "dead_interval": {"required": False, "type": "int"},
"hello-interval": {"required": False, "type": "int"}, "hello_interval": {"required": False, "type": "int"},
"name": {"required": True, "type": "str"}, "name": {"required": True, "type": "str"},
"peer": {"required": False, "type": "str"}, "peer": {"required": False, "type": "str"},
"retransmit-interval": {"required": False, "type": "int"}, "retransmit_interval": {"required": False, "type": "int"},
"transmit-delay": {"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", "bfd": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"default-information-metric": {"required": False, "type": "int"}, "default_information_metric": {"required": False, "type": "int"},
"default-information-metric-type": {"required": False, "type": "str", "default_information_metric_type": {"required": False, "type": "str",
"choices": ["1", "2"]}, "choices": ["1", "2"]},
"default-information-originate": {"required": False, "type": "str", "default_information_originate": {"required": False, "type": "str",
"choices": ["enable", "always", "disable"]}, "choices": ["enable", "always", "disable"]},
"default-information-route-map": {"required": False, "type": "str"}, "default_information_route_map": {"required": False, "type": "str"},
"default-metric": {"required": False, "type": "int"}, "default_metric": {"required": False, "type": "int"},
"log-neighbour-changes": {"required": False, "type": "str", "log_neighbour_changes": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"ospf6-interface": {"required": False, "type": "list", "ospf6_interface": {"required": False, "type": "list",
"options": { "options": {
"area-id": {"required": False, "type": "str"}, "area_id": {"required": False, "type": "str"},
"bfd": {"required": False, "type": "str", "bfd": {"required": False, "type": "str",
"choices": ["global", "enable", "disable"]}, "choices": ["global", "enable", "disable"]},
"cost": {"required": False, "type": "int"}, "cost": {"required": False, "type": "int"},
"dead-interval": {"required": False, "type": "int"}, "dead_interval": {"required": False, "type": "int"},
"hello-interval": {"required": False, "type": "int"}, "hello_interval": {"required": False, "type": "int"},
"interface": {"required": False, "type": "str"}, "interface": {"required": False, "type": "str"},
"mtu": {"required": False, "type": "int"},
"mtu_ignore": {"required": False, "type": "str",
"choices": ["enable", "disable"]},
"name": {"required": True, "type": "str"}, "name": {"required": True, "type": "str"},
"neighbor": {"required": False, "type": "list", "neighbor": {"required": False, "type": "list",
"options": { "options": {
"cost": {"required": False, "type": "int"}, "cost": {"required": False, "type": "int"},
"ip6": {"required": True, "type": "str"}, "ip6": {"required": True, "type": "str"},
"poll-interval": {"required": False, "type": "int"}, "poll_interval": {"required": False, "type": "int"},
"priority": {"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", "choices": ["broadcast", "point-to-point", "non-broadcast",
"point-to-multipoint", "point-to-multipoint-non-broadcast"]}, "point-to-multipoint", "point-to-multipoint-non-broadcast"]},
"priority": {"required": False, "type": "int"}, "priority": {"required": False, "type": "int"},
"retransmit-interval": {"required": False, "type": "int"}, "retransmit_interval": {"required": False, "type": "int"},
"status": {"required": False, "type": "str", "status": {"required": False, "type": "str",
"choices": ["disable", "enable"]}, "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": { "options": {
"name": {"required": True, "type": "str"} "name": {"required": True, "type": "str"}
}}, }},
"redistribute": {"required": False, "type": "list", "redistribute": {"required": False, "type": "list",
"options": { "options": {
"metric": {"required": False, "type": "int"}, "metric": {"required": False, "type": "int"},
"metric-type": {"required": False, "type": "str", "metric_type": {"required": False, "type": "str",
"choices": ["1", "2"]}, "choices": ["1", "2"]},
"name": {"required": True, "type": "str"}, "name": {"required": True, "type": "str"},
"routemap": {"required": False, "type": "str"}, "routemap": {"required": False, "type": "str"},
"status": {"required": False, "type": "str", "status": {"required": False, "type": "str",
"choices": ["enable", "disable"]} "choices": ["enable", "disable"]}
}}, }},
"router-id": {"required": False, "type": "str"}, "router_id": {"required": False, "type": "str"},
"spf-timers": {"required": False, "type": "str"}, "spf_timers": {"required": False, "type": "str"},
"summary-address": {"required": False, "type": "list", "summary_address": {"required": False, "type": "list",
"options": { "options": {
"advertise": {"required": False, "type": "str", "advertise": {"required": False, "type": "str",
"choices": ["disable", "enable"]}, "choices": ["disable", "enable"]},
@ -675,15 +768,31 @@ def main():
module = AnsibleModule(argument_spec=fields, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_router_policy module: fortios_router_policy
short_description: Configure IPv4 routing policies in Fortinet's FortiOS and FortiGate. short_description: Configure IPv4 routing policies in Fortinet's FortiOS and FortiGate.
description: 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. user to set and modify router feature and policy category.
Examples include all parameters and values need to be adjusted to datasources before usage. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,138 +41,175 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true 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: router_policy:
description: description:
- Configure IPv4 routing policies. - Configure IPv4 routing policies.
default: null default: null
type: dict
suboptions: suboptions:
state:
description:
- Indicates whether to create or remove the object
choices:
- present
- absent
action: action:
description: description:
- Action of the policy route. - Action of the policy route.
type: str
choices: choices:
- deny - deny
- permit - permit
comments: comments:
description: description:
- Optional comments. - Optional comments.
type: str
dst: dst:
description: description:
- Destination IP and mask (x.x.x.x/x). - Destination IP and mask (x.x.x.x/x).
type: list
suboptions: suboptions:
subnet: subnet:
description: description:
- IP and mask. - IP and mask.
required: true required: true
dst-negate: type: str
dst_negate:
description: description:
- Enable/disable negating destination address match. - Enable/disable negating destination address match.
type: str
choices: choices:
- enable - enable
- disable - disable
dstaddr: dstaddr:
description: description:
- Destination address name. - Destination address name.
type: list
suboptions: suboptions:
name: name:
description: description:
- Address/group name. Source firewall.address.name firewall.addrgrp.name. - Address/group name. Source firewall.address.name firewall.addrgrp.name.
required: true required: true
end-port: type: str
end_port:
description: description:
- End destination port number (0 - 65535). - End destination port number (0 - 65535).
end-source-port: type: int
end_source_port:
description: description:
- End source port number (0 - 65535). - End source port number (0 - 65535).
type: int
gateway: gateway:
description: description:
- IP address of the gateway. - IP address of the gateway.
input-device: type: str
input_device:
description: description:
- Incoming interface name. - Incoming interface name.
type: list
suboptions: suboptions:
name: name:
description: description:
- Interface name. Source system.interface.name. - Interface name. Source system.interface.name.
required: true required: true
output-device: type: str
output_device:
description: description:
- Outgoing interface name. Source system.interface.name. - Outgoing interface name. Source system.interface.name.
type: str
protocol: protocol:
description: description:
- Protocol number (0 - 255). - Protocol number (0 - 255).
seq-num: type: int
seq_num:
description: description:
- Sequence number. - Sequence number.
required: true type: int
src: src:
description: description:
- Source IP and mask (x.x.x.x/x). - Source IP and mask (x.x.x.x/x).
type: list
suboptions: suboptions:
subnet: subnet:
description: description:
- IP and mask. - IP and mask.
required: true required: true
src-negate: type: str
src_negate:
description: description:
- Enable/disable negating source address match. - Enable/disable negating source address match.
type: str
choices: choices:
- enable - enable
- disable - disable
srcaddr: srcaddr:
description: description:
- Source address name. - Source address name.
type: list
suboptions: suboptions:
name: name:
description: description:
- Address/group name. Source firewall.address.name firewall.addrgrp.name. - Address/group name. Source firewall.address.name firewall.addrgrp.name.
required: true required: true
start-port: type: str
start_port:
description: description:
- Start destination port number (0 - 65535). - Start destination port number (0 - 65535).
start-source-port: type: int
start_source_port:
description: description:
- Start source port number (0 - 65535). - Start source port number (0 - 65535).
type: int
status: status:
description: description:
- Enable/disable this policy route. - Enable/disable this policy route.
type: str
choices: choices:
- enable - enable
- disable - disable
tos: tos:
description: description:
- Type of service bit pattern. - Type of service bit pattern.
tos-mask: type: str
tos_mask:
description: description:
- Type of service evaluated bits. - Type of service evaluated bits.
type: str
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -185,6 +219,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Configure IPv4 routing policies. - name: Configure IPv4 routing policies.
fortios_router_policy: fortios_router_policy:
@ -193,38 +228,38 @@ EXAMPLES = '''
password: "{{ password }}" password: "{{ password }}"
vdom: "{{ vdom }}" vdom: "{{ vdom }}"
https: "False" https: "False"
state: "present"
router_policy: router_policy:
state: "present"
action: "deny" action: "deny"
comments: "<your_own_value>" comments: "<your_own_value>"
dst: dst:
- -
subnet: "<your_own_value>" subnet: "<your_own_value>"
dst-negate: "enable" dst_negate: "enable"
dstaddr: dstaddr:
- -
name: "default_name_9 (source firewall.address.name firewall.addrgrp.name)" name: "default_name_9 (source firewall.address.name firewall.addrgrp.name)"
end-port: "10" end_port: "10"
end-source-port: "11" end_source_port: "11"
gateway: "<your_own_value>" gateway: "<your_own_value>"
input-device: input_device:
- -
name: "default_name_14 (source system.interface.name)" name: "default_name_14 (source system.interface.name)"
output-device: "<your_own_value> (source system.interface.name)" output_device: "<your_own_value> (source system.interface.name)"
protocol: "16" protocol: "16"
seq-num: "17" seq_num: "17"
src: src:
- -
subnet: "<your_own_value>" subnet: "<your_own_value>"
src-negate: "enable" src_negate: "enable"
srcaddr: srcaddr:
- -
name: "default_name_22 (source firewall.address.name firewall.addrgrp.name)" name: "default_name_22 (source firewall.address.name firewall.addrgrp.name)"
start-port: "23" start_port: "23"
start-source-port: "24" start_source_port: "24"
status: "enable" status: "enable"
tos: "<your_own_value>" tos: "<your_own_value>"
tos-mask: "<your_own_value>" tos_mask: "<your_own_value>"
''' '''
RETURN = ''' RETURN = '''
@ -287,14 +322,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -302,17 +339,17 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_router_policy_data(json): def filter_router_policy_data(json):
option_list = ['action', 'comments', 'dst', option_list = ['action', 'comments', 'dst',
'dst-negate', 'dstaddr', 'end-port', 'dst_negate', 'dstaddr', 'end_port',
'end-source-port', 'gateway', 'input-device', 'end_source_port', 'gateway', 'input_device',
'output-device', 'protocol', 'seq-num', 'output_device', 'protocol', 'seq_num',
'src', 'src-negate', 'srcaddr', 'src', 'src_negate', 'srcaddr',
'start-port', 'start-source-port', 'status', 'start_port', 'start_source_port', 'status',
'tos', 'tos-mask'] 'tos', 'tos_mask']
dictionary = {} dictionary = {}
for attribute in option_list: for attribute in option_list:
@ -322,61 +359,66 @@ def filter_router_policy_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
def router_policy(data, fos): def router_policy(data, fos):
vdom = data['vdom'] vdom = data['vdom']
state = data['state']
router_policy_data = data['router_policy'] router_policy_data = data['router_policy']
flattened_data = flatten_multilists_attributes(router_policy_data) filtered_data = underscore_to_hyphen(filter_router_policy_data(router_policy_data))
filtered_data = filter_router_policy_data(flattened_data)
if router_policy_data['state'] == "present": if state == "present":
return fos.set('router', return fos.set('router',
'policy', 'policy',
data=filtered_data, data=filtered_data,
vdom=vdom) vdom=vdom)
elif router_policy_data['state'] == "absent": elif state == "absent":
return fos.delete('router', return fos.delete('router',
'policy', 'policy',
mkey=filtered_data['seq-num'], mkey=filtered_data['seq-num'],
vdom=vdom) 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): def fortios_router(data, fos):
login(data)
if data['router_policy']: if data['router_policy']:
resp = router_policy(data, fos) resp = router_policy(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "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": { "router_policy": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"state": {"required": True, "type": "str",
"choices": ["present", "absent"]},
"action": {"required": False, "type": "str", "action": {"required": False, "type": "str",
"choices": ["deny", "permit"]}, "choices": ["deny", "permit"]},
"comments": {"required": False, "type": "str"}, "comments": {"required": False, "type": "str"},
@ -384,38 +426,38 @@ def main():
"options": { "options": {
"subnet": {"required": True, "type": "str"} "subnet": {"required": True, "type": "str"}
}}, }},
"dst-negate": {"required": False, "type": "str", "dst_negate": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"dstaddr": {"required": False, "type": "list", "dstaddr": {"required": False, "type": "list",
"options": { "options": {
"name": {"required": True, "type": "str"} "name": {"required": True, "type": "str"}
}}, }},
"end-port": {"required": False, "type": "int"}, "end_port": {"required": False, "type": "int"},
"end-source-port": {"required": False, "type": "int"}, "end_source_port": {"required": False, "type": "int"},
"gateway": {"required": False, "type": "str"}, "gateway": {"required": False, "type": "str"},
"input-device": {"required": False, "type": "list", "input_device": {"required": False, "type": "list",
"options": { "options": {
"name": {"required": True, "type": "str"} "name": {"required": True, "type": "str"}
}}, }},
"output-device": {"required": False, "type": "str"}, "output_device": {"required": False, "type": "str"},
"protocol": {"required": False, "type": "int"}, "protocol": {"required": False, "type": "int"},
"seq-num": {"required": True, "type": "int"}, "seq_num": {"required": False, "type": "int"},
"src": {"required": False, "type": "list", "src": {"required": False, "type": "list",
"options": { "options": {
"subnet": {"required": True, "type": "str"} "subnet": {"required": True, "type": "str"}
}}, }},
"src-negate": {"required": False, "type": "str", "src_negate": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"srcaddr": {"required": False, "type": "list", "srcaddr": {"required": False, "type": "list",
"options": { "options": {
"name": {"required": True, "type": "str"} "name": {"required": True, "type": "str"}
}}, }},
"start-port": {"required": False, "type": "int"}, "start_port": {"required": False, "type": "int"},
"start-source-port": {"required": False, "type": "int"}, "start_source_port": {"required": False, "type": "int"},
"status": {"required": False, "type": "str", "status": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"tos": {"required": False, "type": "str"}, "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, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_router_policy6 module: fortios_router_policy6
short_description: Configure IPv6 routing policies in Fortinet's FortiOS and FortiGate. short_description: Configure IPv6 routing policies in Fortinet's FortiOS and FortiGate.
description: 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. user to set and modify router feature and policy6 category.
Examples include all parameters and values need to be adjusted to datasources before usage. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,83 +41,108 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true 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: router_policy6:
description: description:
- Configure IPv6 routing policies. - Configure IPv6 routing policies.
default: null default: null
type: dict
suboptions: suboptions:
state:
description:
- Indicates whether to create or remove the object
choices:
- present
- absent
comments: comments:
description: description:
- Optional comments. - Optional comments.
type: str
dst: dst:
description: description:
- Destination IPv6 prefix. - Destination IPv6 prefix.
end-port: type: str
end_port:
description: description:
- End destination port number (1 - 65535). - End destination port number (1 - 65535).
type: int
gateway: gateway:
description: description:
- IPv6 address of the gateway. - IPv6 address of the gateway.
input-device: type: str
input_device:
description: description:
- Incoming interface name. Source system.interface.name. - Incoming interface name. Source system.interface.name.
output-device: type: str
output_device:
description: description:
- Outgoing interface name. Source system.interface.name. - Outgoing interface name. Source system.interface.name.
type: str
protocol: protocol:
description: description:
- Protocol number (0 - 255). - Protocol number (0 - 255).
seq-num: type: int
seq_num:
description: description:
- Sequence number. - Sequence number.
required: true type: int
src: src:
description: description:
- Source IPv6 prefix. - Source IPv6 prefix.
start-port: type: str
start_port:
description: description:
- Start destination port number (1 - 65535). - Start destination port number (1 - 65535).
type: int
status: status:
description: description:
- Enable/disable this policy route. - Enable/disable this policy route.
type: str
choices: choices:
- enable - enable
- disable - disable
tos: tos:
description: description:
- Type of service bit pattern. - Type of service bit pattern.
tos-mask: type: str
tos_mask:
description: description:
- Type of service evaluated bits. - Type of service evaluated bits.
type: str
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -130,6 +152,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Configure IPv6 routing policies. - name: Configure IPv6 routing policies.
fortios_router_policy6: fortios_router_policy6:
@ -138,21 +161,21 @@ EXAMPLES = '''
password: "{{ password }}" password: "{{ password }}"
vdom: "{{ vdom }}" vdom: "{{ vdom }}"
https: "False" https: "False"
state: "present"
router_policy6: router_policy6:
state: "present"
comments: "<your_own_value>" comments: "<your_own_value>"
dst: "<your_own_value>" dst: "<your_own_value>"
end-port: "5" end_port: "5"
gateway: "<your_own_value>" gateway: "<your_own_value>"
input-device: "<your_own_value> (source system.interface.name)" input_device: "<your_own_value> (source system.interface.name)"
output-device: "<your_own_value> (source system.interface.name)" output_device: "<your_own_value> (source system.interface.name)"
protocol: "9" protocol: "9"
seq-num: "10" seq_num: "10"
src: "<your_own_value>" src: "<your_own_value>"
start-port: "12" start_port: "12"
status: "enable" status: "enable"
tos: "<your_own_value>" tos: "<your_own_value>"
tos-mask: "<your_own_value>" tos_mask: "<your_own_value>"
''' '''
RETURN = ''' RETURN = '''
@ -215,14 +238,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -230,15 +255,15 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_router_policy6_data(json): def filter_router_policy6_data(json):
option_list = ['comments', 'dst', 'end-port', option_list = ['comments', 'dst', 'end_port',
'gateway', 'input-device', 'output-device', 'gateway', 'input_device', 'output_device',
'protocol', 'seq-num', 'src', 'protocol', 'seq_num', 'src',
'start-port', 'status', 'tos', 'start_port', 'status', 'tos',
'tos-mask'] 'tos_mask']
dictionary = {} dictionary = {}
for attribute in option_list: for attribute in option_list:
@ -248,75 +273,80 @@ def filter_router_policy6_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
def router_policy6(data, fos): def router_policy6(data, fos):
vdom = data['vdom'] vdom = data['vdom']
state = data['state']
router_policy6_data = data['router_policy6'] router_policy6_data = data['router_policy6']
flattened_data = flatten_multilists_attributes(router_policy6_data) filtered_data = underscore_to_hyphen(filter_router_policy6_data(router_policy6_data))
filtered_data = filter_router_policy6_data(flattened_data)
if router_policy6_data['state'] == "present": if state == "present":
return fos.set('router', return fos.set('router',
'policy6', 'policy6',
data=filtered_data, data=filtered_data,
vdom=vdom) vdom=vdom)
elif router_policy6_data['state'] == "absent": elif state == "absent":
return fos.delete('router', return fos.delete('router',
'policy6', 'policy6',
mkey=filtered_data['seq-num'], mkey=filtered_data['seq-num'],
vdom=vdom) 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): def fortios_router(data, fos):
login(data)
if data['router_policy6']: if data['router_policy6']:
resp = router_policy6(data, fos) resp = router_policy6(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "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": { "router_policy6": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"state": {"required": True, "type": "str",
"choices": ["present", "absent"]},
"comments": {"required": False, "type": "str"}, "comments": {"required": False, "type": "str"},
"dst": {"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"}, "gateway": {"required": False, "type": "str"},
"input-device": {"required": False, "type": "str"}, "input_device": {"required": False, "type": "str"},
"output-device": {"required": False, "type": "str"}, "output_device": {"required": False, "type": "str"},
"protocol": {"required": False, "type": "int"}, "protocol": {"required": False, "type": "int"},
"seq-num": {"required": True, "type": "int"}, "seq_num": {"required": False, "type": "int"},
"src": {"required": False, "type": "str"}, "src": {"required": False, "type": "str"},
"start-port": {"required": False, "type": "int"}, "start_port": {"required": False, "type": "int"},
"status": {"required": False, "type": "str", "status": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"tos": {"required": False, "type": "str"}, "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, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_router_prefix_list module: fortios_router_prefix_list
short_description: Configure IPv4 prefix lists in Fortinet's FortiOS and FortiGate. short_description: Configure IPv4 prefix lists in Fortinet's FortiOS and FortiGate.
description: 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. 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. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,73 +41,95 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true 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: router_prefix_list:
description: description:
- Configure IPv4 prefix lists. - Configure IPv4 prefix lists.
default: null default: null
type: dict
suboptions: suboptions:
state:
description:
- Indicates whether to create or remove the object
choices:
- present
- absent
comments: comments:
description: description:
- Comment. - Comment.
type: str
name: name:
description: description:
- Name. - Name.
required: true required: true
type: str
rule: rule:
description: description:
- IPv4 prefix list rule. - IPv4 prefix list rule.
type: list
suboptions: suboptions:
action: action:
description: description:
- Permit or deny this IP address and netmask prefix. - Permit or deny this IP address and netmask prefix.
type: str
choices: choices:
- permit - permit
- deny - deny
flags: flags:
description: description:
- Flags. - Flags.
type: int
ge: ge:
description: description:
- Minimum prefix length to be matched (0 - 32). - Minimum prefix length to be matched (0 - 32).
type: int
id: id:
description: description:
- Rule ID. - Rule ID.
required: true required: true
type: int
le: le:
description: description:
- Maximum prefix length to be matched (0 - 32). - Maximum prefix length to be matched (0 - 32).
type: int
prefix: prefix:
description: description:
- IPv4 prefix to define regular filter criteria, such as "any" or subnets. - IPv4 prefix to define regular filter criteria, such as "any" or subnets.
type: str
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -120,6 +139,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Configure IPv4 prefix lists. - name: Configure IPv4 prefix lists.
fortios_router_prefix_list: fortios_router_prefix_list:
@ -128,8 +148,8 @@ EXAMPLES = '''
password: "{{ password }}" password: "{{ password }}"
vdom: "{{ vdom }}" vdom: "{{ vdom }}"
https: "False" https: "False"
state: "present"
router_prefix_list: router_prefix_list:
state: "present"
comments: "<your_own_value>" comments: "<your_own_value>"
name: "default_name_4" name: "default_name_4"
rule: rule:
@ -202,14 +222,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -217,7 +239,7 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_router_prefix_list_data(json): def filter_router_prefix_list_data(json):
@ -231,61 +253,66 @@ def filter_router_prefix_list_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
def router_prefix_list(data, fos): def router_prefix_list(data, fos):
vdom = data['vdom'] vdom = data['vdom']
state = data['state']
router_prefix_list_data = data['router_prefix_list'] router_prefix_list_data = data['router_prefix_list']
flattened_data = flatten_multilists_attributes(router_prefix_list_data) filtered_data = underscore_to_hyphen(filter_router_prefix_list_data(router_prefix_list_data))
filtered_data = filter_router_prefix_list_data(flattened_data)
if router_prefix_list_data['state'] == "present": if state == "present":
return fos.set('router', return fos.set('router',
'prefix-list', 'prefix-list',
data=filtered_data, data=filtered_data,
vdom=vdom) vdom=vdom)
elif router_prefix_list_data['state'] == "absent": elif state == "absent":
return fos.delete('router', return fos.delete('router',
'prefix-list', 'prefix-list',
mkey=filtered_data['name'], mkey=filtered_data['name'],
vdom=vdom) 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): def fortios_router(data, fos):
login(data)
if data['router_prefix_list']: if data['router_prefix_list']:
resp = router_prefix_list(data, fos) resp = router_prefix_list(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "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": { "router_prefix_list": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"state": {"required": True, "type": "str",
"choices": ["present", "absent"]},
"comments": {"required": False, "type": "str"}, "comments": {"required": False, "type": "str"},
"name": {"required": True, "type": "str"}, "name": {"required": True, "type": "str"},
"rule": {"required": False, "type": "list", "rule": {"required": False, "type": "list",
@ -305,15 +332,31 @@ def main():
module = AnsibleModule(argument_spec=fields, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_router_rip module: fortios_router_rip
short_description: Configure RIP in Fortinet's FortiOS and FortiGate. short_description: Configure RIP in Fortinet's FortiOS and FortiGate.
description: 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. user to set and modify router feature and rip category.
Examples include all parameters and values need to be adjusted to datasources before usage. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,67 +41,86 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true default: true
version_added: 2.9
router_rip: router_rip:
description: description:
- Configure RIP. - Configure RIP.
default: null default: null
type: dict
suboptions: suboptions:
default-information-originate: default_information_originate:
description: description:
- Enable/disable generation of default route. - Enable/disable generation of default route.
type: str
choices: choices:
- enable - enable
- disable - disable
default-metric: default_metric:
description: description:
- Default metric. - Default metric.
type: int
distance: distance:
description: description:
- distance - distance
type: list
suboptions: suboptions:
access-list: access_list:
description: description:
- Access list for route destination. Source router.access-list.name. - Access list for route destination. Source router.access-list.name.
type: str
distance: distance:
description: description:
- Distance (1 - 255). - Distance (1 - 255).
type: int
id: id:
description: description:
- Distance ID. - Distance ID.
required: true required: true
type: int
prefix: prefix:
description: description:
- Distance prefix. - Distance prefix.
distribute-list: type: str
distribute_list:
description: description:
- Distribute list. - Distribute list.
type: list
suboptions: suboptions:
direction: direction:
description: description:
- Distribute list direction. - Distribute list direction.
type: str
choices: choices:
- in - in
- out - out
@ -112,110 +128,136 @@ options:
description: description:
- Distribute list ID. - Distribute list ID.
required: true required: true
type: int
interface: interface:
description: description:
- Distribute list interface name. Source system.interface.name. - Distribute list interface name. Source system.interface.name.
type: str
listname: listname:
description: description:
- Distribute access/prefix list name. Source router.access-list.name router.prefix-list.name. - Distribute access/prefix list name. Source router.access-list.name router.prefix-list.name.
type: str
status: status:
description: description:
- status - status
type: str
choices: choices:
- enable - enable
- disable - disable
garbage-timer: garbage_timer:
description: description:
- Garbage timer in seconds. - Garbage timer in seconds.
type: int
interface: interface:
description: description:
- RIP interface configuration. - RIP interface configuration.
type: list
suboptions: suboptions:
auth-keychain: auth_keychain:
description: description:
- Authentication key-chain name. Source router.key-chain.name. - Authentication key-chain name. Source router.key-chain.name.
auth-mode: type: str
auth_mode:
description: description:
- Authentication mode. - Authentication mode.
type: str
choices: choices:
- none - none
- text - text
- md5 - md5
auth-string: auth_string:
description: description:
- Authentication string/password. - Authentication string/password.
type: str
flags: flags:
description: description:
- flags - flags
type: int
name: name:
description: description:
- Interface name. Source system.interface.name. - Interface name. Source system.interface.name.
required: true required: true
receive-version: type: str
receive_version:
description: description:
- Receive version. - Receive version.
type: str
choices: choices:
- 1 - 1
- 2 - 2
send-version: send_version:
description: description:
- Send version. - Send version.
type: str
choices: choices:
- 1 - 1
- 2 - 2
send-version2-broadcast: send_version2_broadcast:
description: description:
- Enable/disable broadcast version 1 compatible packets. - Enable/disable broadcast version 1 compatible packets.
type: str
choices: choices:
- disable - disable
- enable - enable
split-horizon: split_horizon:
description: description:
- Enable/disable split horizon. - Enable/disable split horizon.
type: str
choices: choices:
- poisoned - poisoned
- regular - regular
split-horizon-status: split_horizon_status:
description: description:
- Enable/disable split horizon. - Enable/disable split horizon.
type: str
choices: choices:
- enable - enable
- disable - disable
max-out-metric: max_out_metric:
description: description:
- Maximum metric allowed to output(0 means 'not set'). - Maximum metric allowed to output(0 means 'not set').
type: int
neighbor: neighbor:
description: description:
- neighbor - neighbor
type: list
suboptions: suboptions:
id: id:
description: description:
- Neighbor entry ID. - Neighbor entry ID.
required: true required: true
type: int
ip: ip:
description: description:
- IP address. - IP address.
type: str
network: network:
description: description:
- network - network
type: list
suboptions: suboptions:
id: id:
description: description:
- Network entry ID. - Network entry ID.
required: true required: true
type: int
prefix: prefix:
description: description:
- Network prefix. - Network prefix.
offset-list: type: str
offset_list:
description: description:
- Offset list. - Offset list.
type: list
suboptions: suboptions:
access-list: access_list:
description: description:
- Access list name. Source router.access-list.name. - Access list name. Source router.access-list.name.
type: str
direction: direction:
description: description:
- Offset list direction. - Offset list direction.
type: str
choices: choices:
- in - in
- out - out
@ -223,58 +265,73 @@ options:
description: description:
- Offset-list ID. - Offset-list ID.
required: true required: true
type: int
interface: interface:
description: description:
- Interface name. Source system.interface.name. - Interface name. Source system.interface.name.
type: str
offset: offset:
description: description:
- offset - offset
type: int
status: status:
description: description:
- status - status
type: str
choices: choices:
- enable - enable
- disable - disable
passive-interface: passive_interface:
description: description:
- Passive interface configuration. - Passive interface configuration.
type: list
suboptions: suboptions:
name: name:
description: description:
- Passive interface name. Source system.interface.name. - Passive interface name. Source system.interface.name.
required: true required: true
recv-buffer-size: type: str
recv_buffer_size:
description: description:
- Receiving buffer size. - Receiving buffer size.
type: int
redistribute: redistribute:
description: description:
- Redistribute configuration. - Redistribute configuration.
type: list
suboptions: suboptions:
metric: metric:
description: description:
- Redistribute metric setting. - Redistribute metric setting.
type: int
name: name:
description: description:
- Redistribute name. - Redistribute name.
required: true required: true
type: str
routemap: routemap:
description: description:
- Route map name. Source router.route-map.name. - Route map name. Source router.route-map.name.
type: str
status: status:
description: description:
- status - status
type: str
choices: choices:
- enable - enable
- disable - disable
timeout-timer: timeout_timer:
description: description:
- Timeout timer in seconds. - Timeout timer in seconds.
update-timer: type: int
update_timer:
description: description:
- Update timer in seconds. - Update timer in seconds.
type: int
version: version:
description: description:
- RIP version. - RIP version.
type: str
choices: choices:
- 1 - 1
- 2 - 2
@ -287,6 +344,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Configure RIP. - name: Configure RIP.
fortios_router_rip: fortios_router_rip:
@ -296,35 +354,35 @@ EXAMPLES = '''
vdom: "{{ vdom }}" vdom: "{{ vdom }}"
https: "False" https: "False"
router_rip: router_rip:
default-information-originate: "enable" default_information_originate: "enable"
default-metric: "4" default_metric: "4"
distance: distance:
- -
access-list: "<your_own_value> (source router.access-list.name)" access_list: "<your_own_value> (source router.access-list.name)"
distance: "7" distance: "7"
id: "8" id: "8"
prefix: "<your_own_value>" prefix: "<your_own_value>"
distribute-list: distribute_list:
- -
direction: "in" direction: "in"
id: "12" id: "12"
interface: "<your_own_value> (source system.interface.name)" interface: "<your_own_value> (source system.interface.name)"
listname: "<your_own_value> (source router.access-list.name router.prefix-list.name)" listname: "<your_own_value> (source router.access-list.name router.prefix-list.name)"
status: "enable" status: "enable"
garbage-timer: "16" garbage_timer: "16"
interface: interface:
- -
auth-keychain: "<your_own_value> (source router.key-chain.name)" auth_keychain: "<your_own_value> (source router.key-chain.name)"
auth-mode: "none" auth_mode: "none"
auth-string: "<your_own_value>" auth_string: "<your_own_value>"
flags: "21" flags: "21"
name: "default_name_22 (source system.interface.name)" name: "default_name_22 (source system.interface.name)"
receive-version: "1" receive_version: "1"
send-version: "1" send_version: "1"
send-version2-broadcast: "disable" send_version2_broadcast: "disable"
split-horizon: "poisoned" split_horizon: "poisoned"
split-horizon-status: "enable" split_horizon_status: "enable"
max-out-metric: "28" max_out_metric: "28"
neighbor: neighbor:
- -
id: "30" id: "30"
@ -333,26 +391,26 @@ EXAMPLES = '''
- -
id: "33" id: "33"
prefix: "<your_own_value>" prefix: "<your_own_value>"
offset-list: offset_list:
- -
access-list: "<your_own_value> (source router.access-list.name)" access_list: "<your_own_value> (source router.access-list.name)"
direction: "in" direction: "in"
id: "38" id: "38"
interface: "<your_own_value> (source system.interface.name)" interface: "<your_own_value> (source system.interface.name)"
offset: "40" offset: "40"
status: "enable" status: "enable"
passive-interface: passive_interface:
- -
name: "default_name_43 (source system.interface.name)" name: "default_name_43 (source system.interface.name)"
recv-buffer-size: "44" recv_buffer_size: "44"
redistribute: redistribute:
- -
metric: "46" metric: "46"
name: "default_name_47" name: "default_name_47"
routemap: "<your_own_value> (source router.route-map.name)" routemap: "<your_own_value> (source router.route-map.name)"
status: "enable" status: "enable"
timeout-timer: "50" timeout_timer: "50"
update-timer: "51" update_timer: "51"
version: "1" version: "1"
''' '''
@ -416,14 +474,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -431,15 +491,15 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_router_rip_data(json): def filter_router_rip_data(json):
option_list = ['default-information-originate', 'default-metric', 'distance', option_list = ['default_information_originate', 'default_metric', 'distance',
'distribute-list', 'garbage-timer', 'interface', 'distribute_list', 'garbage_timer', 'interface',
'max-out-metric', 'neighbor', 'network', 'max_out_metric', 'neighbor', 'network',
'offset-list', 'passive-interface', 'recv-buffer-size', 'offset_list', 'passive_interface', 'recv_buffer_size',
'redistribute', 'timeout-timer', 'update-timer', 'redistribute', 'timeout_timer', 'update_timer',
'version'] 'version']
dictionary = {} dictionary = {}
@ -450,17 +510,15 @@ def filter_router_rip_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
@ -468,45 +526,51 @@ def flatten_multilists_attributes(data):
def router_rip(data, fos): def router_rip(data, fos):
vdom = data['vdom'] vdom = data['vdom']
router_rip_data = data['router_rip'] router_rip_data = data['router_rip']
flattened_data = flatten_multilists_attributes(router_rip_data) filtered_data = underscore_to_hyphen(filter_router_rip_data(router_rip_data))
filtered_data = filter_router_rip_data(flattened_data)
return fos.set('router', return fos.set('router',
'rip', 'rip',
data=filtered_data, data=filtered_data,
vdom=vdom) 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): def fortios_router(data, fos):
login(data)
if data['router_rip']: if data['router_rip']:
resp = router_rip(data, fos) resp = router_rip(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "https": {"required": False, "type": "bool", "default": True},
"ssl_verify": {"required": False, "type": "bool", "default": True},
"router_rip": { "router_rip": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"default-information-originate": {"required": False, "type": "str", "default_information_originate": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"default-metric": {"required": False, "type": "int"}, "default_metric": {"required": False, "type": "int"},
"distance": {"required": False, "type": "list", "distance": {"required": False, "type": "list",
"options": { "options": {
"access-list": {"required": False, "type": "str"}, "access_list": {"required": False, "type": "str"},
"distance": {"required": False, "type": "int"}, "distance": {"required": False, "type": "int"},
"id": {"required": True, "type": "int"}, "id": {"required": True, "type": "int"},
"prefix": {"required": False, "type": "str"} "prefix": {"required": False, "type": "str"}
}}, }},
"distribute-list": {"required": False, "type": "list", "distribute_list": {"required": False, "type": "list",
"options": { "options": {
"direction": {"required": False, "type": "str", "direction": {"required": False, "type": "str",
"choices": ["in", "out"]}, "choices": ["in", "out"]},
@ -516,27 +580,27 @@ def main():
"status": {"required": False, "type": "str", "status": {"required": False, "type": "str",
"choices": ["enable", "disable"]} "choices": ["enable", "disable"]}
}}, }},
"garbage-timer": {"required": False, "type": "int"}, "garbage_timer": {"required": False, "type": "int"},
"interface": {"required": False, "type": "list", "interface": {"required": False, "type": "list",
"options": { "options": {
"auth-keychain": {"required": False, "type": "str"}, "auth_keychain": {"required": False, "type": "str"},
"auth-mode": {"required": False, "type": "str", "auth_mode": {"required": False, "type": "str",
"choices": ["none", "text", "md5"]}, "choices": ["none", "text", "md5"]},
"auth-string": {"required": False, "type": "str"}, "auth_string": {"required": False, "type": "str"},
"flags": {"required": False, "type": "int"}, "flags": {"required": False, "type": "int"},
"name": {"required": True, "type": "str"}, "name": {"required": True, "type": "str"},
"receive-version": {"required": False, "type": "str", "receive_version": {"required": False, "type": "str",
"choices": ["1", "2"]}, "choices": ["1", "2"]},
"send-version": {"required": False, "type": "str", "send_version": {"required": False, "type": "str",
"choices": ["1", "2"]}, "choices": ["1", "2"]},
"send-version2-broadcast": {"required": False, "type": "str", "send_version2_broadcast": {"required": False, "type": "str",
"choices": ["disable", "enable"]}, "choices": ["disable", "enable"]},
"split-horizon": {"required": False, "type": "str", "split_horizon": {"required": False, "type": "str",
"choices": ["poisoned", "regular"]}, "choices": ["poisoned", "regular"]},
"split-horizon-status": {"required": False, "type": "str", "split_horizon_status": {"required": False, "type": "str",
"choices": ["enable", "disable"]} "choices": ["enable", "disable"]}
}}, }},
"max-out-metric": {"required": False, "type": "int"}, "max_out_metric": {"required": False, "type": "int"},
"neighbor": {"required": False, "type": "list", "neighbor": {"required": False, "type": "list",
"options": { "options": {
"id": {"required": True, "type": "int"}, "id": {"required": True, "type": "int"},
@ -547,9 +611,9 @@ def main():
"id": {"required": True, "type": "int"}, "id": {"required": True, "type": "int"},
"prefix": {"required": False, "type": "str"} "prefix": {"required": False, "type": "str"}
}}, }},
"offset-list": {"required": False, "type": "list", "offset_list": {"required": False, "type": "list",
"options": { "options": {
"access-list": {"required": False, "type": "str"}, "access_list": {"required": False, "type": "str"},
"direction": {"required": False, "type": "str", "direction": {"required": False, "type": "str",
"choices": ["in", "out"]}, "choices": ["in", "out"]},
"id": {"required": True, "type": "int"}, "id": {"required": True, "type": "int"},
@ -558,11 +622,11 @@ def main():
"status": {"required": False, "type": "str", "status": {"required": False, "type": "str",
"choices": ["enable", "disable"]} "choices": ["enable", "disable"]}
}}, }},
"passive-interface": {"required": False, "type": "list", "passive_interface": {"required": False, "type": "list",
"options": { "options": {
"name": {"required": True, "type": "str"} "name": {"required": True, "type": "str"}
}}, }},
"recv-buffer-size": {"required": False, "type": "int"}, "recv_buffer_size": {"required": False, "type": "int"},
"redistribute": {"required": False, "type": "list", "redistribute": {"required": False, "type": "list",
"options": { "options": {
"metric": {"required": False, "type": "int"}, "metric": {"required": False, "type": "int"},
@ -571,8 +635,8 @@ def main():
"status": {"required": False, "type": "str", "status": {"required": False, "type": "str",
"choices": ["enable", "disable"]} "choices": ["enable", "disable"]}
}}, }},
"timeout-timer": {"required": False, "type": "int"}, "timeout_timer": {"required": False, "type": "int"},
"update-timer": {"required": False, "type": "int"}, "update_timer": {"required": False, "type": "int"},
"version": {"required": False, "type": "str", "version": {"required": False, "type": "str",
"choices": ["1", "2"]} "choices": ["1", "2"]}
@ -582,15 +646,31 @@ def main():
module = AnsibleModule(argument_spec=fields, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_router_setting module: fortios_router_setting
short_description: Configure router settings in Fortinet's FortiOS and FortiGate. short_description: Configure router settings in Fortinet's FortiOS and FortiGate.
description: 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. user to set and modify router feature and setting category.
Examples include all parameters and values need to be adjusted to datasources before usage. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,40 +41,52 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true default: true
version_added: 2.9
router_setting: router_setting:
description: description:
- Configure router settings. - Configure router settings.
default: null default: null
type: dict
suboptions: suboptions:
hostname: hostname:
description: description:
- Hostname for this virtual domain router. - Hostname for this virtual domain router.
show-filter: type: str
show_filter:
description: description:
- Prefix-list as filter for showing routes. Source router.prefix-list.name. - Prefix-list as filter for showing routes. Source router.prefix-list.name.
type: str
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -87,6 +96,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Configure router settings. - name: Configure router settings.
fortios_router_setting: fortios_router_setting:
@ -97,7 +107,7 @@ EXAMPLES = '''
https: "False" https: "False"
router_setting: router_setting:
hostname: "myhostname" hostname: "myhostname"
show-filter: "<your_own_value> (source router.prefix-list.name)" show_filter: "<your_own_value> (source router.prefix-list.name)"
''' '''
RETURN = ''' RETURN = '''
@ -160,14 +170,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -175,11 +187,11 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_router_setting_data(json): def filter_router_setting_data(json):
option_list = ['hostname', 'show-filter'] option_list = ['hostname', 'show_filter']
dictionary = {} dictionary = {}
for attribute in option_list: for attribute in option_list:
@ -189,17 +201,15 @@ def filter_router_setting_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
@ -207,36 +217,42 @@ def flatten_multilists_attributes(data):
def router_setting(data, fos): def router_setting(data, fos):
vdom = data['vdom'] vdom = data['vdom']
router_setting_data = data['router_setting'] router_setting_data = data['router_setting']
flattened_data = flatten_multilists_attributes(router_setting_data) filtered_data = underscore_to_hyphen(filter_router_setting_data(router_setting_data))
filtered_data = filter_router_setting_data(flattened_data)
return fos.set('router', return fos.set('router',
'setting', 'setting',
data=filtered_data, data=filtered_data,
vdom=vdom) 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): def fortios_router(data, fos):
login(data)
if data['router_setting']: if data['router_setting']:
resp = router_setting(data, fos) resp = router_setting(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "https": {"required": False, "type": "bool", "default": True},
"ssl_verify": {"required": False, "type": "bool", "default": True},
"router_setting": { "router_setting": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"hostname": {"required": False, "type": "str"}, "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, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -14,9 +14,6 @@ from __future__ import (absolute_import, division, print_function)
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# the lib use python logging can get it if the following is set in your
# Ansible config.
__metaclass__ = type __metaclass__ = type
@ -29,10 +26,10 @@ DOCUMENTATION = '''
module: fortios_router_static module: fortios_router_static
short_description: Configure IPv4 static routing tables in Fortinet's FortiOS and FortiGate. short_description: Configure IPv4 static routing tables in Fortinet's FortiOS and FortiGate.
description: 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. user to set and modify router feature and static category.
Examples include all parameters and values need to be adjusted to datasources before usage. 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" version_added: "2.8"
author: author:
- Miguel Angel Munoz (@mamunozgonzalez) - Miguel Angel Munoz (@mamunozgonzalez)
@ -44,116 +41,147 @@ requirements:
- fortiosapi>=0.9.8 - fortiosapi>=0.9.8
options: options:
host: host:
description: description:
- FortiOS or FortiGate ip address. - FortiOS or FortiGate IP address.
required: true type: str
required: false
username: username:
description: description:
- FortiOS or FortiGate username. - FortiOS or FortiGate username.
required: true type: str
required: false
password: password:
description: description:
- FortiOS or FortiGate password. - FortiOS or FortiGate password.
type: str
default: "" default: ""
vdom: vdom:
description: description:
- Virtual domain, among those defined previously. A vdom is a - Virtual domain, among those defined previously. A vdom is a
virtual instance of the FortiGate that can be configured and virtual instance of the FortiGate that can be configured and
used as a different unit. used as a different unit.
type: str
default: root default: root
https: https:
description: description:
- Indicates if the requests towards FortiGate must use HTTPS - Indicates if the requests towards FortiGate must use HTTPS protocol.
protocol type: bool
default: true
ssl_verify:
description:
- Ensures FortiGate certificate must be verified by a proper CA.
type: bool type: bool
default: true 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: router_static:
description: description:
- Configure IPv4 static routing tables. - Configure IPv4 static routing tables.
default: null default: null
type: dict
suboptions: suboptions:
state:
description:
- Indicates whether to create or remove the object
choices:
- present
- absent
bfd: bfd:
description: description:
- Enable/disable Bidirectional Forwarding Detection (BFD). - Enable/disable Bidirectional Forwarding Detection (BFD).
type: str
choices: choices:
- enable - enable
- disable - disable
blackhole: blackhole:
description: description:
- Enable/disable black hole. - Enable/disable black hole.
type: str
choices: choices:
- enable - enable
- disable - disable
comment: comment:
description: description:
- Optional comments. - Optional comments.
type: str
device: device:
description: description:
- Gateway out interface or tunnel. Source system.interface.name. - Gateway out interface or tunnel. Source system.interface.name.
type: str
distance: distance:
description: description:
- Administrative distance (1 - 255). - Administrative distance (1 - 255).
type: int
dst: dst:
description: description:
- Destination IP and mask for this route. - Destination IP and mask for this route.
type: str
dstaddr: dstaddr:
description: description:
- Name of firewall address or address group. Source firewall.address.name firewall.addrgrp.name. - Name of firewall address or address group. Source firewall.address.name firewall.addrgrp.name.
dynamic-gateway: type: str
dynamic_gateway:
description: description:
- Enable use of dynamic gateway retrieved from a DHCP or PPP server. - Enable use of dynamic gateway retrieved from a DHCP or PPP server.
type: str
choices: choices:
- enable - enable
- disable - disable
gateway: gateway:
description: description:
- Gateway IP for this route. - Gateway IP for this route.
internet-service: type: str
internet_service:
description: description:
- Application ID in the Internet service database. Source firewall.internet-service.id. - Application ID in the Internet service database. Source firewall.internet-service.id.
internet-service-custom: type: int
internet_service_custom:
description: description:
- Application name in the Internet service custom database. Source firewall.internet-service-custom.name. - Application name in the Internet service custom database. Source firewall.internet-service-custom.name.
link-monitor-exempt: type: str
link_monitor_exempt:
description: description:
- Enable/disable withdrawing this route when link monitor or health check is down. - Enable/disable withdrawing this route when link monitor or health check is down.
type: str
choices: choices:
- enable - enable
- disable - disable
priority: priority:
description: description:
- Administrative priority (0 - 4294967295). - Administrative priority (0 - 4294967295).
seq-num: type: int
seq_num:
description: description:
- Sequence number. - Sequence number.
required: true type: int
src: src:
description: description:
- Source prefix for this route. - Source prefix for this route.
type: str
status: status:
description: description:
- Enable/disable this static route. - Enable/disable this static route.
type: str
choices: choices:
- enable - enable
- disable - disable
virtual-wan-link: virtual_wan_link:
description: description:
- Enable/disable egress through the virtual-wan-link. - Enable/disable egress through the virtual-wan-link.
type: str
choices: choices:
- enable - enable
- disable - disable
vrf: vrf:
description: description:
- Virtual Routing Forwarding ID. - Virtual Routing Forwarding ID.
type: int
weight: weight:
description: description:
- Administrative weight (0 - 255). - Administrative weight (0 - 255).
type: int
''' '''
EXAMPLES = ''' EXAMPLES = '''
@ -163,6 +191,7 @@ EXAMPLES = '''
username: "admin" username: "admin"
password: "" password: ""
vdom: "root" vdom: "root"
ssl_verify: "False"
tasks: tasks:
- name: Configure IPv4 static routing tables. - name: Configure IPv4 static routing tables.
fortios_router_static: fortios_router_static:
@ -171,8 +200,8 @@ EXAMPLES = '''
password: "{{ password }}" password: "{{ password }}"
vdom: "{{ vdom }}" vdom: "{{ vdom }}"
https: "False" https: "False"
state: "present"
router_static: router_static:
state: "present"
bfd: "enable" bfd: "enable"
blackhole: "enable" blackhole: "enable"
comment: "Optional comments." comment: "Optional comments."
@ -180,16 +209,16 @@ EXAMPLES = '''
distance: "7" distance: "7"
dst: "<your_own_value>" dst: "<your_own_value>"
dstaddr: "<your_own_value> (source firewall.address.name firewall.addrgrp.name)" dstaddr: "<your_own_value> (source firewall.address.name firewall.addrgrp.name)"
dynamic-gateway: "enable" dynamic_gateway: "enable"
gateway: "<your_own_value>" gateway: "<your_own_value>"
internet-service: "12 (source firewall.internet-service.id)" internet_service: "12 (source firewall.internet-service.id)"
internet-service-custom: "<your_own_value> (source firewall.internet-service-custom.name)" internet_service_custom: "<your_own_value> (source firewall.internet-service-custom.name)"
link-monitor-exempt: "enable" link_monitor_exempt: "enable"
priority: "15" priority: "15"
seq-num: "16" seq_num: "16"
src: "<your_own_value>" src: "<your_own_value>"
status: "enable" status: "enable"
virtual-wan-link: "enable" virtual_wan_link: "enable"
vrf: "20" vrf: "20"
weight: "21" weight: "21"
''' '''
@ -254,14 +283,16 @@ version:
''' '''
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection
fos = None 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'] host = data['host']
username = data['username'] username = data['username']
password = data['password'] password = data['password']
ssl_verify = data['ssl_verify']
fos.debug('on') fos.debug('on')
if 'https' in data and not data['https']: if 'https' in data and not data['https']:
@ -269,16 +300,16 @@ def login(data):
else: else:
fos.https('on') fos.https('on')
fos.login(host, username, password) fos.login(host, username, password, verify=ssl_verify)
def filter_router_static_data(json): def filter_router_static_data(json):
option_list = ['bfd', 'blackhole', 'comment', option_list = ['bfd', 'blackhole', 'comment',
'device', 'distance', 'dst', 'device', 'distance', 'dst',
'dstaddr', 'dynamic-gateway', 'gateway', 'dstaddr', 'dynamic_gateway', 'gateway',
'internet-service', 'internet-service-custom', 'link-monitor-exempt', 'internet_service', 'internet_service_custom', 'link_monitor_exempt',
'priority', 'seq-num', 'src', 'priority', 'seq_num', 'src',
'status', 'virtual-wan-link', 'vrf', 'status', 'virtual_wan_link', 'vrf',
'weight'] 'weight']
dictionary = {} dictionary = {}
@ -289,61 +320,66 @@ def filter_router_static_data(json):
return dictionary return dictionary
def flatten_multilists_attributes(data): def underscore_to_hyphen(data):
multilist_attrs = [] if isinstance(data, list):
for elem in data:
for attr in multilist_attrs: elem = underscore_to_hyphen(elem)
try: elif isinstance(data, dict):
path = "data['" + "']['".join(elem for elem in attr) + "']" new_data = {}
current_val = eval(path) for k, v in data.items():
flattened_val = ' '.join(elem for elem in current_val) new_data[k.replace('_', '-')] = underscore_to_hyphen(v)
exec(path + '= flattened_val') data = new_data
except BaseException:
pass
return data return data
def router_static(data, fos): def router_static(data, fos):
vdom = data['vdom'] vdom = data['vdom']
state = data['state']
router_static_data = data['router_static'] router_static_data = data['router_static']
flattened_data = flatten_multilists_attributes(router_static_data) filtered_data = underscore_to_hyphen(filter_router_static_data(router_static_data))
filtered_data = filter_router_static_data(flattened_data)
if router_static_data['state'] == "present": if state == "present":
return fos.set('router', return fos.set('router',
'static', 'static',
data=filtered_data, data=filtered_data,
vdom=vdom) vdom=vdom)
elif router_static_data['state'] == "absent": elif state == "absent":
return fos.delete('router', return fos.delete('router',
'static', 'static',
mkey=filtered_data['seq-num'], mkey=filtered_data['seq-num'],
vdom=vdom) 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): def fortios_router(data, fos):
login(data)
if data['router_static']: if data['router_static']:
resp = router_static(data, fos) resp = router_static(data, fos)
fos.logout() return not is_successful_status(resp), \
return not resp['status'] == "success", resp['status'] == "success", resp resp['status'] == "success", \
resp
def main(): def main():
fields = { fields = {
"host": {"required": True, "type": "str"}, "host": {"required": False, "type": "str"},
"username": {"required": True, "type": "str"}, "username": {"required": False, "type": "str"},
"password": {"required": False, "type": "str", "no_log": True}, "password": {"required": False, "type": "str", "default": "", "no_log": True},
"vdom": {"required": False, "type": "str", "default": "root"}, "vdom": {"required": False, "type": "str", "default": "root"},
"https": {"required": False, "type": "bool", "default": True}, "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": { "router_static": {
"required": False, "type": "dict", "required": False, "type": "dict", "default": None,
"options": { "options": {
"state": {"required": True, "type": "str",
"choices": ["present", "absent"]},
"bfd": {"required": False, "type": "str", "bfd": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"blackhole": {"required": False, "type": "str", "blackhole": {"required": False, "type": "str",
@ -353,19 +389,19 @@ def main():
"distance": {"required": False, "type": "int"}, "distance": {"required": False, "type": "int"},
"dst": {"required": False, "type": "str"}, "dst": {"required": False, "type": "str"},
"dstaddr": {"required": False, "type": "str"}, "dstaddr": {"required": False, "type": "str"},
"dynamic-gateway": {"required": False, "type": "str", "dynamic_gateway": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"gateway": {"required": False, "type": "str"}, "gateway": {"required": False, "type": "str"},
"internet-service": {"required": False, "type": "int"}, "internet_service": {"required": False, "type": "int"},
"internet-service-custom": {"required": False, "type": "str"}, "internet_service_custom": {"required": False, "type": "str"},
"link-monitor-exempt": {"required": False, "type": "str", "link_monitor_exempt": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"priority": {"required": False, "type": "int"}, "priority": {"required": False, "type": "int"},
"seq-num": {"required": True, "type": "int"}, "seq_num": {"required": False, "type": "int"},
"src": {"required": False, "type": "str"}, "src": {"required": False, "type": "str"},
"status": {"required": False, "type": "str", "status": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"virtual-wan-link": {"required": False, "type": "str", "virtual_wan_link": {"required": False, "type": "str",
"choices": ["enable", "disable"]}, "choices": ["enable", "disable"]},
"vrf": {"required": False, "type": "int"}, "vrf": {"required": False, "type": "int"},
"weight": {"required": False, "type": "int"} "weight": {"required": False, "type": "int"}
@ -376,15 +412,31 @@ def main():
module = AnsibleModule(argument_spec=fields, module = AnsibleModule(argument_spec=fields,
supports_check_mode=False) supports_check_mode=False)
try:
from fortiosapi import FortiOSAPI
except ImportError:
module.fail_json(msg="fortiosapi module is required")
global fos # legacy_mode refers to using fortiosapi instead of HTTPAPI
fos = FortiOSAPI() 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: if not is_error:
module.exit_json(changed=has_changed, meta=result) module.exit_json(changed=has_changed, meta=result)

@ -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:E337
lib/ansible/modules/network/fortios/fortios_ipv4_policy.py validate-modules:E338 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_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:E336
lib/ansible/modules/network/fortios/fortios_spamfilter_profile.py validate-modules:E337 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 lib/ansible/modules/network/fortios/fortios_ssh_filter_profile.py validate-modules:E336

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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

@ -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 <https://www.gnu.org/licenses/>.
# 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
Loading…
Cancel
Save