1#!/usr/bin/python 2from __future__ import (absolute_import, division, print_function) 3# Copyright 2019 Fortinet, Inc. 4# 5# This program is free software: you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation, either version 3 of the License, or 8# (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program. If not, see <https://www.gnu.org/licenses/>. 17 18__metaclass__ = type 19 20ANSIBLE_METADATA = {'status': ['preview'], 21 'supported_by': 'community', 22 'metadata_version': '1.1'} 23 24DOCUMENTATION = ''' 25--- 26module: fortios_router_prefix_list6 27short_description: Configure IPv6 prefix lists in Fortinet's FortiOS and FortiGate. 28description: 29 - This module is able to configure a FortiGate or FortiOS (FOS) device by allowing the 30 user to set and modify router feature and prefix_list6 category. 31 Examples include all parameters and values need to be adjusted to datasources before usage. 32 Tested with FOS v6.0.5 33version_added: "2.9" 34author: 35 - Miguel Angel Munoz (@mamunozgonzalez) 36 - Nicolas Thomas (@thomnico) 37notes: 38 - Requires fortiosapi library developed by Fortinet 39 - Run as a local_action in your playbook 40requirements: 41 - fortiosapi>=0.9.8 42options: 43 host: 44 description: 45 - FortiOS or FortiGate IP address. 46 type: str 47 required: false 48 username: 49 description: 50 - FortiOS or FortiGate username. 51 type: str 52 required: false 53 password: 54 description: 55 - FortiOS or FortiGate password. 56 type: str 57 default: "" 58 vdom: 59 description: 60 - Virtual domain, among those defined previously. A vdom is a 61 virtual instance of the FortiGate that can be configured and 62 used as a different unit. 63 type: str 64 default: root 65 https: 66 description: 67 - Indicates if the requests towards FortiGate must use HTTPS protocol. 68 type: bool 69 default: true 70 ssl_verify: 71 description: 72 - Ensures FortiGate certificate must be verified by a proper CA. 73 type: bool 74 default: true 75 state: 76 description: 77 - Indicates whether to create or remove the object. 78 type: str 79 required: true 80 choices: 81 - present 82 - absent 83 router_prefix_list6: 84 description: 85 - Configure IPv6 prefix lists. 86 default: null 87 type: dict 88 suboptions: 89 comments: 90 description: 91 - Comment. 92 type: str 93 name: 94 description: 95 - Name. 96 required: true 97 type: str 98 rule: 99 description: 100 - IPv6 prefix list rule. 101 type: list 102 suboptions: 103 action: 104 description: 105 - Permit or deny packets that match this rule. 106 type: str 107 choices: 108 - permit 109 - deny 110 flags: 111 description: 112 - Flags. 113 type: int 114 ge: 115 description: 116 - Minimum prefix length to be matched (0 - 128). 117 type: int 118 id: 119 description: 120 - Rule ID. 121 required: true 122 type: int 123 le: 124 description: 125 - Maximum prefix length to be matched (0 - 128). 126 type: int 127 prefix6: 128 description: 129 - IPv6 prefix to define regular filter criteria, such as "any" or subnets. 130 type: str 131''' 132 133EXAMPLES = ''' 134- hosts: localhost 135 vars: 136 host: "192.168.122.40" 137 username: "admin" 138 password: "" 139 vdom: "root" 140 ssl_verify: "False" 141 tasks: 142 - name: Configure IPv6 prefix lists. 143 fortios_router_prefix_list6: 144 host: "{{ host }}" 145 username: "{{ username }}" 146 password: "{{ password }}" 147 vdom: "{{ vdom }}" 148 https: "False" 149 state: "present" 150 router_prefix_list6: 151 comments: "<your_own_value>" 152 name: "default_name_4" 153 rule: 154 - 155 action: "permit" 156 flags: "7" 157 ge: "8" 158 id: "9" 159 le: "10" 160 prefix6: "<your_own_value>" 161''' 162 163RETURN = ''' 164build: 165 description: Build number of the fortigate image 166 returned: always 167 type: str 168 sample: '1547' 169http_method: 170 description: Last method used to provision the content into FortiGate 171 returned: always 172 type: str 173 sample: 'PUT' 174http_status: 175 description: Last result given by FortiGate on last operation applied 176 returned: always 177 type: str 178 sample: "200" 179mkey: 180 description: Master key (id) used in the last call to FortiGate 181 returned: success 182 type: str 183 sample: "id" 184name: 185 description: Name of the table used to fulfill the request 186 returned: always 187 type: str 188 sample: "urlfilter" 189path: 190 description: Path of the table used to fulfill the request 191 returned: always 192 type: str 193 sample: "webfilter" 194revision: 195 description: Internal revision number 196 returned: always 197 type: str 198 sample: "17.0.2.10658" 199serial: 200 description: Serial number of the unit 201 returned: always 202 type: str 203 sample: "FGVMEVYYQT3AB5352" 204status: 205 description: Indication of the operation's result 206 returned: always 207 type: str 208 sample: "success" 209vdom: 210 description: Virtual domain used 211 returned: always 212 type: str 213 sample: "root" 214version: 215 description: Version of the FortiGate 216 returned: always 217 type: str 218 sample: "v5.6.3" 219 220''' 221 222from ansible.module_utils.basic import AnsibleModule 223from ansible.module_utils.connection import Connection 224from ansible.module_utils.network.fortios.fortios import FortiOSHandler 225from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG 226 227 228def login(data, fos): 229 host = data['host'] 230 username = data['username'] 231 password = data['password'] 232 ssl_verify = data['ssl_verify'] 233 234 fos.debug('on') 235 if 'https' in data and not data['https']: 236 fos.https('off') 237 else: 238 fos.https('on') 239 240 fos.login(host, username, password, verify=ssl_verify) 241 242 243def filter_router_prefix_list6_data(json): 244 option_list = ['comments', 'name', 'rule'] 245 dictionary = {} 246 247 for attribute in option_list: 248 if attribute in json and json[attribute] is not None: 249 dictionary[attribute] = json[attribute] 250 251 return dictionary 252 253 254def underscore_to_hyphen(data): 255 if isinstance(data, list): 256 for elem in data: 257 elem = underscore_to_hyphen(elem) 258 elif isinstance(data, dict): 259 new_data = {} 260 for k, v in data.items(): 261 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 262 data = new_data 263 264 return data 265 266 267def router_prefix_list6(data, fos): 268 vdom = data['vdom'] 269 state = data['state'] 270 router_prefix_list6_data = data['router_prefix_list6'] 271 filtered_data = underscore_to_hyphen(filter_router_prefix_list6_data(router_prefix_list6_data)) 272 273 if state == "present": 274 return fos.set('router', 275 'prefix-list6', 276 data=filtered_data, 277 vdom=vdom) 278 279 elif state == "absent": 280 return fos.delete('router', 281 'prefix-list6', 282 mkey=filtered_data['name'], 283 vdom=vdom) 284 285 286def is_successful_status(status): 287 return status['status'] == "success" or \ 288 status['http_method'] == "DELETE" and status['http_status'] == 404 289 290 291def fortios_router(data, fos): 292 293 if data['router_prefix_list6']: 294 resp = router_prefix_list6(data, fos) 295 296 return not is_successful_status(resp), \ 297 resp['status'] == "success", \ 298 resp 299 300 301def main(): 302 fields = { 303 "host": {"required": False, "type": "str"}, 304 "username": {"required": False, "type": "str"}, 305 "password": {"required": False, "type": "str", "default": "", "no_log": True}, 306 "vdom": {"required": False, "type": "str", "default": "root"}, 307 "https": {"required": False, "type": "bool", "default": True}, 308 "ssl_verify": {"required": False, "type": "bool", "default": True}, 309 "state": {"required": True, "type": "str", 310 "choices": ["present", "absent"]}, 311 "router_prefix_list6": { 312 "required": False, "type": "dict", "default": None, 313 "options": { 314 "comments": {"required": False, "type": "str"}, 315 "name": {"required": True, "type": "str"}, 316 "rule": {"required": False, "type": "list", 317 "options": { 318 "action": {"required": False, "type": "str", 319 "choices": ["permit", "deny"]}, 320 "flags": {"required": False, "type": "int"}, 321 "ge": {"required": False, "type": "int"}, 322 "id": {"required": True, "type": "int"}, 323 "le": {"required": False, "type": "int"}, 324 "prefix6": {"required": False, "type": "str"} 325 }} 326 327 } 328 } 329 } 330 331 module = AnsibleModule(argument_spec=fields, 332 supports_check_mode=False) 333 334 # legacy_mode refers to using fortiosapi instead of HTTPAPI 335 legacy_mode = 'host' in module.params and module.params['host'] is not None and \ 336 'username' in module.params and module.params['username'] is not None and \ 337 'password' in module.params and module.params['password'] is not None 338 339 if not legacy_mode: 340 if module._socket_path: 341 connection = Connection(module._socket_path) 342 fos = FortiOSHandler(connection) 343 344 is_error, has_changed, result = fortios_router(module.params, fos) 345 else: 346 module.fail_json(**FAIL_SOCKET_MSG) 347 else: 348 try: 349 from fortiosapi import FortiOSAPI 350 except ImportError: 351 module.fail_json(msg="fortiosapi module is required") 352 353 fos = FortiOSAPI() 354 355 login(module.params, fos) 356 is_error, has_changed, result = fortios_router(module.params, fos) 357 fos.logout() 358 359 if not is_error: 360 module.exit_json(changed=has_changed, meta=result) 361 else: 362 module.fail_json(msg="Error in repo", meta=result) 363 364 365if __name__ == '__main__': 366 main() 367