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_list 27short_description: Configure IPv4 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_list category. 31 Examples include all parameters and values need to be adjusted to datasources before usage. 32 Tested with FOS v6.0.5 33version_added: "2.8" 34author: 35 - Miguel Angel Munoz (@mamunozgonzalez) 36 - Nicolas Thomas (@thomnico) 37notes: 38 - Requires fortiosapi library developed by Fortinet 39 - Run as a local_action in your playbook 40requirements: 41 - fortiosapi>=0.9.8 42options: 43 host: 44 description: 45 - FortiOS or FortiGate IP address. 46 type: str 47 required: false 48 username: 49 description: 50 - FortiOS or FortiGate username. 51 type: str 52 required: false 53 password: 54 description: 55 - FortiOS or FortiGate password. 56 type: str 57 default: "" 58 vdom: 59 description: 60 - Virtual domain, among those defined previously. A vdom is a 61 virtual instance of the FortiGate that can be configured and 62 used as a different unit. 63 type: str 64 default: root 65 https: 66 description: 67 - Indicates if the requests towards FortiGate must use HTTPS protocol. 68 type: bool 69 default: true 70 ssl_verify: 71 description: 72 - Ensures FortiGate certificate must be verified by a proper CA. 73 type: bool 74 default: true 75 version_added: 2.9 76 state: 77 description: 78 - Indicates whether to create or remove the object. 79 This attribute was present already in previous version in a deeper level. 80 It has been moved out to this outer level. 81 type: str 82 required: false 83 choices: 84 - present 85 - absent 86 version_added: 2.9 87 router_prefix_list: 88 description: 89 - Configure IPv4 prefix lists. 90 default: null 91 type: dict 92 suboptions: 93 state: 94 description: 95 - B(Deprecated) 96 - Starting with Ansible 2.9 we recommend using the top-level 'state' parameter. 97 - HORIZONTALLINE 98 - Indicates whether to create or remove the object. 99 type: str 100 required: false 101 choices: 102 - present 103 - absent 104 comments: 105 description: 106 - Comment. 107 type: str 108 name: 109 description: 110 - Name. 111 required: true 112 type: str 113 rule: 114 description: 115 - IPv4 prefix list rule. 116 type: list 117 suboptions: 118 action: 119 description: 120 - Permit or deny this IP address and netmask prefix. 121 type: str 122 choices: 123 - permit 124 - deny 125 flags: 126 description: 127 - Flags. 128 type: int 129 ge: 130 description: 131 - Minimum prefix length to be matched (0 - 32). 132 type: int 133 id: 134 description: 135 - Rule ID. 136 required: true 137 type: int 138 le: 139 description: 140 - Maximum prefix length to be matched (0 - 32). 141 type: int 142 prefix: 143 description: 144 - IPv4 prefix to define regular filter criteria, such as "any" or subnets. 145 type: str 146''' 147 148EXAMPLES = ''' 149- hosts: localhost 150 vars: 151 host: "192.168.122.40" 152 username: "admin" 153 password: "" 154 vdom: "root" 155 ssl_verify: "False" 156 tasks: 157 - name: Configure IPv4 prefix lists. 158 fortios_router_prefix_list: 159 host: "{{ host }}" 160 username: "{{ username }}" 161 password: "{{ password }}" 162 vdom: "{{ vdom }}" 163 https: "False" 164 state: "present" 165 router_prefix_list: 166 comments: "<your_own_value>" 167 name: "default_name_4" 168 rule: 169 - 170 action: "permit" 171 flags: "7" 172 ge: "8" 173 id: "9" 174 le: "10" 175 prefix: "<your_own_value>" 176''' 177 178RETURN = ''' 179build: 180 description: Build number of the fortigate image 181 returned: always 182 type: str 183 sample: '1547' 184http_method: 185 description: Last method used to provision the content into FortiGate 186 returned: always 187 type: str 188 sample: 'PUT' 189http_status: 190 description: Last result given by FortiGate on last operation applied 191 returned: always 192 type: str 193 sample: "200" 194mkey: 195 description: Master key (id) used in the last call to FortiGate 196 returned: success 197 type: str 198 sample: "id" 199name: 200 description: Name of the table used to fulfill the request 201 returned: always 202 type: str 203 sample: "urlfilter" 204path: 205 description: Path of the table used to fulfill the request 206 returned: always 207 type: str 208 sample: "webfilter" 209revision: 210 description: Internal revision number 211 returned: always 212 type: str 213 sample: "17.0.2.10658" 214serial: 215 description: Serial number of the unit 216 returned: always 217 type: str 218 sample: "FGVMEVYYQT3AB5352" 219status: 220 description: Indication of the operation's result 221 returned: always 222 type: str 223 sample: "success" 224vdom: 225 description: Virtual domain used 226 returned: always 227 type: str 228 sample: "root" 229version: 230 description: Version of the FortiGate 231 returned: always 232 type: str 233 sample: "v5.6.3" 234 235''' 236 237from ansible.module_utils.basic import AnsibleModule 238from ansible.module_utils.connection import Connection 239from ansible.module_utils.network.fortios.fortios import FortiOSHandler 240from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG 241 242 243def login(data, fos): 244 host = data['host'] 245 username = data['username'] 246 password = data['password'] 247 ssl_verify = data['ssl_verify'] 248 249 fos.debug('on') 250 if 'https' in data and not data['https']: 251 fos.https('off') 252 else: 253 fos.https('on') 254 255 fos.login(host, username, password, verify=ssl_verify) 256 257 258def filter_router_prefix_list_data(json): 259 option_list = ['comments', 'name', 'rule'] 260 dictionary = {} 261 262 for attribute in option_list: 263 if attribute in json and json[attribute] is not None: 264 dictionary[attribute] = json[attribute] 265 266 return dictionary 267 268 269def underscore_to_hyphen(data): 270 if isinstance(data, list): 271 for elem in data: 272 elem = underscore_to_hyphen(elem) 273 elif isinstance(data, dict): 274 new_data = {} 275 for k, v in data.items(): 276 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 277 data = new_data 278 279 return data 280 281 282def router_prefix_list(data, fos): 283 vdom = data['vdom'] 284 if 'state' in data and data['state']: 285 state = data['state'] 286 elif 'state' in data['router_prefix_list'] and data['router_prefix_list']: 287 state = data['router_prefix_list']['state'] 288 else: 289 state = True 290 router_prefix_list_data = data['router_prefix_list'] 291 filtered_data = underscore_to_hyphen(filter_router_prefix_list_data(router_prefix_list_data)) 292 293 if state == "present": 294 return fos.set('router', 295 'prefix-list', 296 data=filtered_data, 297 vdom=vdom) 298 299 elif state == "absent": 300 return fos.delete('router', 301 'prefix-list', 302 mkey=filtered_data['name'], 303 vdom=vdom) 304 305 306def is_successful_status(status): 307 return status['status'] == "success" or \ 308 status['http_method'] == "DELETE" and status['http_status'] == 404 309 310 311def fortios_router(data, fos): 312 313 if data['router_prefix_list']: 314 resp = router_prefix_list(data, fos) 315 316 return not is_successful_status(resp), \ 317 resp['status'] == "success", \ 318 resp 319 320 321def main(): 322 fields = { 323 "host": {"required": False, "type": "str"}, 324 "username": {"required": False, "type": "str"}, 325 "password": {"required": False, "type": "str", "default": "", "no_log": True}, 326 "vdom": {"required": False, "type": "str", "default": "root"}, 327 "https": {"required": False, "type": "bool", "default": True}, 328 "ssl_verify": {"required": False, "type": "bool", "default": True}, 329 "state": {"required": False, "type": "str", 330 "choices": ["present", "absent"]}, 331 "router_prefix_list": { 332 "required": False, "type": "dict", "default": None, 333 "options": { 334 "state": {"required": False, "type": "str", 335 "choices": ["present", "absent"]}, 336 "comments": {"required": False, "type": "str"}, 337 "name": {"required": True, "type": "str"}, 338 "rule": {"required": False, "type": "list", 339 "options": { 340 "action": {"required": False, "type": "str", 341 "choices": ["permit", "deny"]}, 342 "flags": {"required": False, "type": "int"}, 343 "ge": {"required": False, "type": "int"}, 344 "id": {"required": True, "type": "int"}, 345 "le": {"required": False, "type": "int"}, 346 "prefix": {"required": False, "type": "str"} 347 }} 348 349 } 350 } 351 } 352 353 module = AnsibleModule(argument_spec=fields, 354 supports_check_mode=False) 355 356 # legacy_mode refers to using fortiosapi instead of HTTPAPI 357 legacy_mode = 'host' in module.params and module.params['host'] is not None and \ 358 'username' in module.params and module.params['username'] is not None and \ 359 'password' in module.params and module.params['password'] is not None 360 361 if not legacy_mode: 362 if module._socket_path: 363 connection = Connection(module._socket_path) 364 fos = FortiOSHandler(connection) 365 366 is_error, has_changed, result = fortios_router(module.params, fos) 367 else: 368 module.fail_json(**FAIL_SOCKET_MSG) 369 else: 370 try: 371 from fortiosapi import FortiOSAPI 372 except ImportError: 373 module.fail_json(msg="fortiosapi module is required") 374 375 fos = FortiOSAPI() 376 377 login(module.params, fos) 378 is_error, has_changed, result = fortios_router(module.params, fos) 379 fos.logout() 380 381 if not is_error: 382 module.exit_json(changed=has_changed, meta=result) 383 else: 384 module.fail_json(msg="Error in repo", meta=result) 385 386 387if __name__ == '__main__': 388 main() 389