1#!/usr/local/bin/python3.8 2from __future__ import (absolute_import, division, print_function) 3# Copyright 2019-2020 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_vpn_l2tp 27short_description: Configure L2TP 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 vpn feature and l2tp category. 31 Examples include all parameters and values need to be adjusted to datasources before usage. 32 Tested with FOS v6.0.0 33version_added: "2.10" 34author: 35 - Link Zheng (@chillancezen) 36 - Jie Xue (@JieX19) 37 - Hongbin Lu (@fgtdev-hblu) 38 - Frank Shen (@frankshen01) 39 - Miguel Angel Munoz (@mamunozgonzalez) 40 - Nicolas Thomas (@thomnico) 41notes: 42 - Legacy fortiosapi has been deprecated, httpapi is the preferred way to run playbooks 43 44requirements: 45 - ansible>=2.9.0 46options: 47 access_token: 48 description: 49 - Token-based authentication. 50 Generated from GUI of Fortigate. 51 type: str 52 required: false 53 enable_log: 54 description: 55 - Enable/Disable logging for task. 56 type: bool 57 required: false 58 default: false 59 vdom: 60 description: 61 - Virtual domain, among those defined previously. A vdom is a 62 virtual instance of the FortiGate that can be configured and 63 used as a different unit. 64 type: str 65 default: root 66 67 vpn_l2tp: 68 description: 69 - Configure L2TP. 70 default: null 71 type: dict 72 suboptions: 73 compress: 74 description: 75 - Enable/disable data compression. 76 type: str 77 choices: 78 - enable 79 - disable 80 eip: 81 description: 82 - End IP. 83 type: str 84 enforce_ipsec: 85 description: 86 - Enable/disable IPsec enforcement. 87 type: str 88 choices: 89 - enable 90 - disable 91 lcp_echo_interval: 92 description: 93 - Time in seconds between PPPoE Link Control Protocol (LCP) echo requests. 94 type: int 95 lcp_max_echo_fails: 96 description: 97 - Maximum number of missed LCP echo messages before disconnect. 98 type: int 99 sip: 100 description: 101 - Start IP. 102 type: str 103 status: 104 description: 105 - Enable/disable FortiGate as a L2TP gateway. 106 type: str 107 choices: 108 - enable 109 - disable 110 usrgrp: 111 description: 112 - User group. Source user.group.name. 113 type: str 114''' 115 116EXAMPLES = ''' 117- hosts: fortigates 118 collections: 119 - fortinet.fortios 120 connection: httpapi 121 vars: 122 vdom: "root" 123 ansible_httpapi_use_ssl: yes 124 ansible_httpapi_validate_certs: no 125 ansible_httpapi_port: 443 126 tasks: 127 - name: Configure L2TP. 128 fortios_vpn_l2tp: 129 vdom: "{{ vdom }}" 130 vpn_l2tp: 131 compress: "enable" 132 eip: "<your_own_value>" 133 enforce_ipsec: "enable" 134 lcp_echo_interval: "6" 135 lcp_max_echo_fails: "7" 136 sip: "<your_own_value>" 137 status: "enable" 138 usrgrp: "<your_own_value> (source user.group.name)" 139 140''' 141 142RETURN = ''' 143build: 144 description: Build number of the fortigate image 145 returned: always 146 type: str 147 sample: '1547' 148http_method: 149 description: Last method used to provision the content into FortiGate 150 returned: always 151 type: str 152 sample: 'PUT' 153http_status: 154 description: Last result given by FortiGate on last operation applied 155 returned: always 156 type: str 157 sample: "200" 158mkey: 159 description: Master key (id) used in the last call to FortiGate 160 returned: success 161 type: str 162 sample: "id" 163name: 164 description: Name of the table used to fulfill the request 165 returned: always 166 type: str 167 sample: "urlfilter" 168path: 169 description: Path of the table used to fulfill the request 170 returned: always 171 type: str 172 sample: "webfilter" 173revision: 174 description: Internal revision number 175 returned: always 176 type: str 177 sample: "17.0.2.10658" 178serial: 179 description: Serial number of the unit 180 returned: always 181 type: str 182 sample: "FGVMEVYYQT3AB5352" 183status: 184 description: Indication of the operation's result 185 returned: always 186 type: str 187 sample: "success" 188vdom: 189 description: Virtual domain used 190 returned: always 191 type: str 192 sample: "root" 193version: 194 description: Version of the FortiGate 195 returned: always 196 type: str 197 sample: "v5.6.3" 198 199''' 200from ansible.module_utils.basic import AnsibleModule 201from ansible.module_utils.connection import Connection 202from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import FortiOSHandler 203from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_legacy_fortiosapi 204from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import schema_to_module_spec 205from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.fortios import check_schema_versioning 206from ansible_collections.fortinet.fortios.plugins.module_utils.fortimanager.common import FAIL_SOCKET_MSG 207from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import is_same_comparison 208from ansible_collections.fortinet.fortios.plugins.module_utils.fortios.comparison import serialize 209 210 211def filter_vpn_l2tp_data(json): 212 option_list = ['compress', 'eip', 'enforce_ipsec', 213 'lcp_echo_interval', 'lcp_max_echo_fails', 'sip', 214 'status', 'usrgrp'] 215 dictionary = {} 216 217 for attribute in option_list: 218 if attribute in json and json[attribute] is not None: 219 dictionary[attribute] = json[attribute] 220 221 return dictionary 222 223 224def underscore_to_hyphen(data): 225 if isinstance(data, list): 226 for i, elem in enumerate(data): 227 data[i] = underscore_to_hyphen(elem) 228 elif isinstance(data, dict): 229 new_data = {} 230 for k, v in data.items(): 231 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 232 data = new_data 233 234 return data 235 236 237def vpn_l2tp(data, fos): 238 vdom = data['vdom'] 239 vpn_l2tp_data = data['vpn_l2tp'] 240 filtered_data = underscore_to_hyphen(filter_vpn_l2tp_data(vpn_l2tp_data)) 241 242 return fos.set('vpn', 243 'l2tp', 244 data=filtered_data, 245 vdom=vdom) 246 247 248def is_successful_status(status): 249 return status['status'] == "success" or \ 250 status['http_method'] == "DELETE" and status['http_status'] == 404 251 252 253def fortios_vpn(data, fos): 254 255 if data['vpn_l2tp']: 256 resp = vpn_l2tp(data, fos) 257 else: 258 fos._module.fail_json(msg='missing task body: %s' % ('vpn_l2tp')) 259 260 return not is_successful_status(resp), \ 261 resp['status'] == "success" and \ 262 (resp['revision_changed'] if 'revision_changed' in resp else True), \ 263 resp 264 265 266versioned_schema = { 267 "type": "dict", 268 "children": { 269 "status": { 270 "type": "string", 271 "options": [ 272 { 273 "value": "enable", 274 "revisions": { 275 "v6.0.0": True, 276 "v7.0.0": True, 277 "v6.0.5": True, 278 "v6.4.4": True, 279 "v6.4.0": True, 280 "v6.4.1": True, 281 "v6.2.0": True, 282 "v6.2.3": True, 283 "v6.2.5": True, 284 "v6.2.7": True, 285 "v6.0.11": True 286 } 287 }, 288 { 289 "value": "disable", 290 "revisions": { 291 "v6.0.0": True, 292 "v7.0.0": True, 293 "v6.0.5": True, 294 "v6.4.4": True, 295 "v6.4.0": True, 296 "v6.4.1": True, 297 "v6.2.0": True, 298 "v6.2.3": True, 299 "v6.2.5": True, 300 "v6.2.7": True, 301 "v6.0.11": True 302 } 303 } 304 ], 305 "revisions": { 306 "v6.0.0": True, 307 "v7.0.0": True, 308 "v6.0.5": True, 309 "v6.4.4": True, 310 "v6.4.0": True, 311 "v6.4.1": True, 312 "v6.2.0": True, 313 "v6.2.3": True, 314 "v6.2.5": True, 315 "v6.2.7": True, 316 "v6.0.11": True 317 } 318 }, 319 "eip": { 320 "type": "string", 321 "revisions": { 322 "v6.0.0": True, 323 "v7.0.0": True, 324 "v6.0.5": True, 325 "v6.4.4": True, 326 "v6.4.0": True, 327 "v6.4.1": True, 328 "v6.2.0": True, 329 "v6.2.3": True, 330 "v6.2.5": True, 331 "v6.2.7": True, 332 "v6.0.11": True 333 } 334 }, 335 "sip": { 336 "type": "string", 337 "revisions": { 338 "v6.0.0": True, 339 "v7.0.0": True, 340 "v6.0.5": True, 341 "v6.4.4": True, 342 "v6.4.0": True, 343 "v6.4.1": True, 344 "v6.2.0": True, 345 "v6.2.3": True, 346 "v6.2.5": True, 347 "v6.2.7": True, 348 "v6.0.11": True 349 } 350 }, 351 "usrgrp": { 352 "type": "string", 353 "revisions": { 354 "v6.0.0": True, 355 "v7.0.0": True, 356 "v6.0.5": True, 357 "v6.4.4": True, 358 "v6.4.0": True, 359 "v6.4.1": True, 360 "v6.2.0": True, 361 "v6.2.3": True, 362 "v6.2.5": True, 363 "v6.2.7": True, 364 "v6.0.11": True 365 } 366 }, 367 "compress": { 368 "type": "string", 369 "options": [ 370 { 371 "value": "enable", 372 "revisions": { 373 "v7.0.0": True, 374 "v6.4.4": True, 375 "v6.4.0": True, 376 "v6.4.1": True, 377 "v6.2.0": True, 378 "v6.2.5": True, 379 "v6.2.7": True 380 } 381 }, 382 { 383 "value": "disable", 384 "revisions": { 385 "v7.0.0": True, 386 "v6.4.4": True, 387 "v6.4.0": True, 388 "v6.4.1": True, 389 "v6.2.0": True, 390 "v6.2.5": True, 391 "v6.2.7": True 392 } 393 } 394 ], 395 "revisions": { 396 "v7.0.0": True, 397 "v6.4.4": True, 398 "v6.4.0": True, 399 "v6.4.1": True, 400 "v6.2.0": True, 401 "v6.2.3": False, 402 "v6.2.5": True, 403 "v6.2.7": True 404 } 405 }, 406 "lcp_echo_interval": { 407 "type": "integer", 408 "revisions": { 409 "v6.4.4": True, 410 "v7.0.0": True, 411 "v6.4.0": True, 412 "v6.4.1": False 413 } 414 }, 415 "enforce_ipsec": { 416 "type": "string", 417 "options": [ 418 { 419 "value": "enable", 420 "revisions": { 421 "v6.0.0": True, 422 "v7.0.0": True, 423 "v6.0.5": True, 424 "v6.4.4": True, 425 "v6.4.0": True, 426 "v6.4.1": True, 427 "v6.2.0": True, 428 "v6.2.3": True, 429 "v6.2.5": True, 430 "v6.2.7": True, 431 "v6.0.11": True 432 } 433 }, 434 { 435 "value": "disable", 436 "revisions": { 437 "v6.0.0": True, 438 "v7.0.0": True, 439 "v6.0.5": True, 440 "v6.4.4": True, 441 "v6.4.0": True, 442 "v6.4.1": True, 443 "v6.2.0": True, 444 "v6.2.3": True, 445 "v6.2.5": True, 446 "v6.2.7": True, 447 "v6.0.11": True 448 } 449 } 450 ], 451 "revisions": { 452 "v6.0.0": True, 453 "v7.0.0": True, 454 "v6.0.5": True, 455 "v6.4.4": True, 456 "v6.4.0": True, 457 "v6.4.1": True, 458 "v6.2.0": True, 459 "v6.2.3": True, 460 "v6.2.5": True, 461 "v6.2.7": True, 462 "v6.0.11": True 463 } 464 }, 465 "lcp_max_echo_fails": { 466 "type": "integer", 467 "revisions": { 468 "v6.4.4": True, 469 "v7.0.0": True, 470 "v6.4.0": True, 471 "v6.4.1": False 472 } 473 } 474 }, 475 "revisions": { 476 "v6.0.0": True, 477 "v7.0.0": True, 478 "v6.0.5": True, 479 "v6.4.4": True, 480 "v6.4.0": True, 481 "v6.4.1": True, 482 "v6.2.0": True, 483 "v6.2.3": True, 484 "v6.2.5": True, 485 "v6.2.7": True, 486 "v6.0.11": True 487 } 488} 489 490 491def main(): 492 module_spec = schema_to_module_spec(versioned_schema) 493 mkeyname = None 494 fields = { 495 "access_token": {"required": False, "type": "str", "no_log": True}, 496 "enable_log": {"required": False, "type": bool}, 497 "vdom": {"required": False, "type": "str", "default": "root"}, 498 "vpn_l2tp": { 499 "required": False, "type": "dict", "default": None, 500 "options": { 501 } 502 } 503 } 504 for attribute_name in module_spec['options']: 505 fields["vpn_l2tp"]['options'][attribute_name] = module_spec['options'][attribute_name] 506 if mkeyname and mkeyname == attribute_name: 507 fields["vpn_l2tp"]['options'][attribute_name]['required'] = True 508 509 check_legacy_fortiosapi() 510 module = AnsibleModule(argument_spec=fields, 511 supports_check_mode=False) 512 513 versions_check_result = None 514 if module._socket_path: 515 connection = Connection(module._socket_path) 516 if 'access_token' in module.params: 517 connection.set_option('access_token', module.params['access_token']) 518 519 if 'enable_log' in module.params: 520 connection.set_option('enable_log', module.params['enable_log']) 521 else: 522 connection.set_option('enable_log', False) 523 fos = FortiOSHandler(connection, module, mkeyname) 524 versions_check_result = check_schema_versioning(fos, versioned_schema, "vpn_l2tp") 525 526 is_error, has_changed, result = fortios_vpn(module.params, fos) 527 528 else: 529 module.fail_json(**FAIL_SOCKET_MSG) 530 531 if versions_check_result and versions_check_result['matched'] is False: 532 module.warn("Ansible has detected version mismatch between FortOS system and your playbook, see more details by specifying option -vvv") 533 534 if not is_error: 535 if versions_check_result and versions_check_result['matched'] is False: 536 module.exit_json(changed=has_changed, version_check_warning=versions_check_result, meta=result) 537 else: 538 module.exit_json(changed=has_changed, meta=result) 539 else: 540 if versions_check_result and versions_check_result['matched'] is False: 541 module.fail_json(msg="Error in repo", version_check_warning=versions_check_result, meta=result) 542 else: 543 module.fail_json(msg="Error in repo", meta=result) 544 545 546if __name__ == '__main__': 547 main() 548