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_system_wccp 27short_description: Configure WCCP 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 system feature and wccp 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 system_wccp: 84 description: 85 - Configure WCCP. 86 default: null 87 type: dict 88 suboptions: 89 assignment_bucket_format: 90 description: 91 - Assignment bucket format for the WCCP cache engine. 92 type: str 93 choices: 94 - wccp-v2 95 - cisco-implementation 96 assignment_dstaddr_mask: 97 description: 98 - Assignment destination address mask. 99 type: str 100 assignment_method: 101 description: 102 - Hash key assignment preference. 103 type: str 104 choices: 105 - HASH 106 - MASK 107 - any 108 assignment_srcaddr_mask: 109 description: 110 - Assignment source address mask. 111 type: str 112 assignment_weight: 113 description: 114 - Assignment of hash weight/ratio for the WCCP cache engine. 115 type: int 116 authentication: 117 description: 118 - Enable/disable MD5 authentication. 119 type: str 120 choices: 121 - enable 122 - disable 123 cache_engine_method: 124 description: 125 - Method used to forward traffic to the routers or to return to the cache engine. 126 type: str 127 choices: 128 - GRE 129 - L2 130 cache_id: 131 description: 132 - IP address known to all routers. If the addresses are the same, use the default 0.0.0.0. 133 type: str 134 forward_method: 135 description: 136 - Method used to forward traffic to the cache servers. 137 type: str 138 choices: 139 - GRE 140 - L2 141 - any 142 group_address: 143 description: 144 - IP multicast address used by the cache routers. For the FortiGate to ignore multicast WCCP traffic, use the default 0.0.0.0. 145 type: str 146 password: 147 description: 148 - Password for MD5 authentication. 149 type: str 150 ports: 151 description: 152 - Service ports. 153 type: str 154 ports_defined: 155 description: 156 - Match method. 157 type: str 158 choices: 159 - source 160 - destination 161 primary_hash: 162 description: 163 - Hash method. 164 type: str 165 choices: 166 - src-ip 167 - dst-ip 168 - src-port 169 - dst-port 170 priority: 171 description: 172 - Service priority. 173 type: int 174 protocol: 175 description: 176 - Service protocol. 177 type: int 178 return_method: 179 description: 180 - Method used to decline a redirected packet and return it to the FortiGate. 181 type: str 182 choices: 183 - GRE 184 - L2 185 - any 186 router_id: 187 description: 188 - IP address known to all cache engines. If all cache engines connect to the same FortiGate interface, use the default 0.0.0.0. 189 type: str 190 router_list: 191 description: 192 - IP addresses of one or more WCCP routers. 193 type: str 194 server_list: 195 description: 196 - IP addresses and netmasks for up to four cache servers. 197 type: str 198 server_type: 199 description: 200 - Cache server type. 201 type: str 202 choices: 203 - forward 204 - proxy 205 service_id: 206 description: 207 - Service ID. 208 type: str 209 service_type: 210 description: 211 - WCCP service type used by the cache server for logical interception and redirection of traffic. 212 type: str 213 choices: 214 - auto 215 - standard 216 - dynamic 217''' 218 219EXAMPLES = ''' 220- hosts: localhost 221 vars: 222 host: "192.168.122.40" 223 username: "admin" 224 password: "" 225 vdom: "root" 226 ssl_verify: "False" 227 tasks: 228 - name: Configure WCCP. 229 fortios_system_wccp: 230 host: "{{ host }}" 231 username: "{{ username }}" 232 password: "{{ password }}" 233 vdom: "{{ vdom }}" 234 https: "False" 235 state: "present" 236 system_wccp: 237 assignment_bucket_format: "wccp-v2" 238 assignment_dstaddr_mask: "<your_own_value>" 239 assignment_method: "HASH" 240 assignment_srcaddr_mask: "<your_own_value>" 241 assignment_weight: "7" 242 authentication: "enable" 243 cache_engine_method: "GRE" 244 cache_id: "<your_own_value>" 245 forward_method: "GRE" 246 group_address: "<your_own_value>" 247 password: "<your_own_value>" 248 ports: "<your_own_value>" 249 ports_defined: "source" 250 primary_hash: "src-ip" 251 priority: "17" 252 protocol: "18" 253 return_method: "GRE" 254 router_id: "<your_own_value>" 255 router_list: "<your_own_value>" 256 server_list: "<your_own_value>" 257 server_type: "forward" 258 service_id: "<your_own_value>" 259 service_type: "auto" 260''' 261 262RETURN = ''' 263build: 264 description: Build number of the fortigate image 265 returned: always 266 type: str 267 sample: '1547' 268http_method: 269 description: Last method used to provision the content into FortiGate 270 returned: always 271 type: str 272 sample: 'PUT' 273http_status: 274 description: Last result given by FortiGate on last operation applied 275 returned: always 276 type: str 277 sample: "200" 278mkey: 279 description: Master key (id) used in the last call to FortiGate 280 returned: success 281 type: str 282 sample: "id" 283name: 284 description: Name of the table used to fulfill the request 285 returned: always 286 type: str 287 sample: "urlfilter" 288path: 289 description: Path of the table used to fulfill the request 290 returned: always 291 type: str 292 sample: "webfilter" 293revision: 294 description: Internal revision number 295 returned: always 296 type: str 297 sample: "17.0.2.10658" 298serial: 299 description: Serial number of the unit 300 returned: always 301 type: str 302 sample: "FGVMEVYYQT3AB5352" 303status: 304 description: Indication of the operation's result 305 returned: always 306 type: str 307 sample: "success" 308vdom: 309 description: Virtual domain used 310 returned: always 311 type: str 312 sample: "root" 313version: 314 description: Version of the FortiGate 315 returned: always 316 type: str 317 sample: "v5.6.3" 318 319''' 320 321from ansible.module_utils.basic import AnsibleModule 322from ansible.module_utils.connection import Connection 323from ansible.module_utils.network.fortios.fortios import FortiOSHandler 324from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG 325 326 327def login(data, fos): 328 host = data['host'] 329 username = data['username'] 330 password = data['password'] 331 ssl_verify = data['ssl_verify'] 332 333 fos.debug('on') 334 if 'https' in data and not data['https']: 335 fos.https('off') 336 else: 337 fos.https('on') 338 339 fos.login(host, username, password, verify=ssl_verify) 340 341 342def filter_system_wccp_data(json): 343 option_list = ['assignment_bucket_format', 'assignment_dstaddr_mask', 'assignment_method', 344 'assignment_srcaddr_mask', 'assignment_weight', 'authentication', 345 'cache_engine_method', 'cache_id', 'forward_method', 346 'group_address', 'password', 'ports', 347 'ports_defined', 'primary_hash', 'priority', 348 'protocol', 'return_method', 'router_id', 349 'router_list', 'server_list', 'server_type', 350 'service_id', 'service_type'] 351 dictionary = {} 352 353 for attribute in option_list: 354 if attribute in json and json[attribute] is not None: 355 dictionary[attribute] = json[attribute] 356 357 return dictionary 358 359 360def underscore_to_hyphen(data): 361 if isinstance(data, list): 362 for elem in data: 363 elem = underscore_to_hyphen(elem) 364 elif isinstance(data, dict): 365 new_data = {} 366 for k, v in data.items(): 367 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 368 data = new_data 369 370 return data 371 372 373def system_wccp(data, fos): 374 vdom = data['vdom'] 375 state = data['state'] 376 system_wccp_data = data['system_wccp'] 377 filtered_data = underscore_to_hyphen(filter_system_wccp_data(system_wccp_data)) 378 379 if state == "present": 380 return fos.set('system', 381 'wccp', 382 data=filtered_data, 383 vdom=vdom) 384 385 elif state == "absent": 386 return fos.delete('system', 387 'wccp', 388 mkey=filtered_data['service-id'], 389 vdom=vdom) 390 391 392def is_successful_status(status): 393 return status['status'] == "success" or \ 394 status['http_method'] == "DELETE" and status['http_status'] == 404 395 396 397def fortios_system(data, fos): 398 399 if data['system_wccp']: 400 resp = system_wccp(data, fos) 401 402 return not is_successful_status(resp), \ 403 resp['status'] == "success", \ 404 resp 405 406 407def main(): 408 fields = { 409 "host": {"required": False, "type": "str"}, 410 "username": {"required": False, "type": "str"}, 411 "password": {"required": False, "type": "str", "default": "", "no_log": True}, 412 "vdom": {"required": False, "type": "str", "default": "root"}, 413 "https": {"required": False, "type": "bool", "default": True}, 414 "ssl_verify": {"required": False, "type": "bool", "default": True}, 415 "state": {"required": True, "type": "str", 416 "choices": ["present", "absent"]}, 417 "system_wccp": { 418 "required": False, "type": "dict", "default": None, 419 "options": { 420 "assignment_bucket_format": {"required": False, "type": "str", 421 "choices": ["wccp-v2", "cisco-implementation"]}, 422 "assignment_dstaddr_mask": {"required": False, "type": "str"}, 423 "assignment_method": {"required": False, "type": "str", 424 "choices": ["HASH", "MASK", "any"]}, 425 "assignment_srcaddr_mask": {"required": False, "type": "str"}, 426 "assignment_weight": {"required": False, "type": "int"}, 427 "authentication": {"required": False, "type": "str", 428 "choices": ["enable", "disable"]}, 429 "cache_engine_method": {"required": False, "type": "str", 430 "choices": ["GRE", "L2"]}, 431 "cache_id": {"required": False, "type": "str"}, 432 "forward_method": {"required": False, "type": "str", 433 "choices": ["GRE", "L2", "any"]}, 434 "group_address": {"required": False, "type": "str"}, 435 "password": {"required": False, "type": "str", "no_log": True}, 436 "ports": {"required": False, "type": "str"}, 437 "ports_defined": {"required": False, "type": "str", 438 "choices": ["source", "destination"]}, 439 "primary_hash": {"required": False, "type": "str", 440 "choices": ["src-ip", "dst-ip", "src-port", 441 "dst-port"]}, 442 "priority": {"required": False, "type": "int"}, 443 "protocol": {"required": False, "type": "int"}, 444 "return_method": {"required": False, "type": "str", 445 "choices": ["GRE", "L2", "any"]}, 446 "router_id": {"required": False, "type": "str"}, 447 "router_list": {"required": False, "type": "str"}, 448 "server_list": {"required": False, "type": "str"}, 449 "server_type": {"required": False, "type": "str", 450 "choices": ["forward", "proxy"]}, 451 "service_id": {"required": False, "type": "str"}, 452 "service_type": {"required": False, "type": "str", 453 "choices": ["auto", "standard", "dynamic"]} 454 455 } 456 } 457 } 458 459 module = AnsibleModule(argument_spec=fields, 460 supports_check_mode=False) 461 462 # legacy_mode refers to using fortiosapi instead of HTTPAPI 463 legacy_mode = 'host' in module.params and module.params['host'] is not None and \ 464 'username' in module.params and module.params['username'] is not None and \ 465 'password' in module.params and module.params['password'] is not None 466 467 if not legacy_mode: 468 if module._socket_path: 469 connection = Connection(module._socket_path) 470 fos = FortiOSHandler(connection) 471 472 is_error, has_changed, result = fortios_system(module.params, fos) 473 else: 474 module.fail_json(**FAIL_SOCKET_MSG) 475 else: 476 try: 477 from fortiosapi import FortiOSAPI 478 except ImportError: 479 module.fail_json(msg="fortiosapi module is required") 480 481 fos = FortiOSAPI() 482 483 login(module.params, fos) 484 is_error, has_changed, result = fortios_system(module.params, fos) 485 fos.logout() 486 487 if not is_error: 488 module.exit_json(changed=has_changed, meta=result) 489 else: 490 module.fail_json(msg="Error in repo", meta=result) 491 492 493if __name__ == '__main__': 494 main() 495