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_switch_controller_lldp_profile 27short_description: Configure FortiSwitch LLDP profiles 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 switch_controller feature and lldp_profile 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 switch_controller_lldp_profile: 88 description: 89 - Configure FortiSwitch LLDP profiles. 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 802.1_tlvs: 105 description: 106 - Transmitted IEEE 802.1 TLVs. 107 type: str 108 choices: 109 - port-vlan-id 110 802.3_tlvs: 111 description: 112 - Transmitted IEEE 802.3 TLVs. 113 type: str 114 choices: 115 - max-frame-size 116 auto_isl: 117 description: 118 - Enable/disable auto inter-switch LAG. 119 type: str 120 choices: 121 - disable 122 - enable 123 auto_isl_hello_timer: 124 description: 125 - Auto inter-switch LAG hello timer duration (1 - 30 sec). 126 type: int 127 auto_isl_port_group: 128 description: 129 - Auto inter-switch LAG port group ID (0 - 9). 130 type: int 131 auto_isl_receive_timeout: 132 description: 133 - Auto inter-switch LAG timeout if no response is received (3 - 90 sec). 134 type: int 135 custom_tlvs: 136 description: 137 - Configuration method to edit custom TLV entries. 138 type: list 139 suboptions: 140 information_string: 141 description: 142 - Organizationally defined information string (0 - 507 hexadecimal bytes). 143 type: str 144 name: 145 description: 146 - TLV name (not sent). 147 required: true 148 type: str 149 oui: 150 description: 151 - Organizationally unique identifier (OUI), a 3-byte hexadecimal number, for this TLV. 152 type: str 153 subtype: 154 description: 155 - Organizationally defined subtype (0 - 255). 156 type: int 157 med_network_policy: 158 description: 159 - Configuration method to edit Media Endpoint Discovery (MED) network policy type-length-value (TLV) categories. 160 type: list 161 suboptions: 162 dscp: 163 description: 164 - Advertised Differentiated Services Code Point (DSCP) value, a packet header value indicating the level of service requested for 165 traffic, such as high priority or best effort delivery. 166 type: int 167 name: 168 description: 169 - Policy type name. 170 required: true 171 type: str 172 priority: 173 description: 174 - Advertised Layer 2 priority (0 - 7; from lowest to highest priority). 175 type: int 176 status: 177 description: 178 - Enable or disable this TLV. 179 type: str 180 choices: 181 - disable 182 - enable 183 vlan: 184 description: 185 - ID of VLAN to advertise, if configured on port (0 - 4094, 0 = priority tag). 186 type: int 187 med_tlvs: 188 description: 189 - "Transmitted LLDP-MED TLVs (type-length-value descriptions): inventory management TLV and/or network policy TLV." 190 type: str 191 choices: 192 - inventory-management 193 - network-policy 194 name: 195 description: 196 - Profile name. 197 required: true 198 type: str 199''' 200 201EXAMPLES = ''' 202- hosts: localhost 203 vars: 204 host: "192.168.122.40" 205 username: "admin" 206 password: "" 207 vdom: "root" 208 ssl_verify: "False" 209 tasks: 210 - name: Configure FortiSwitch LLDP profiles. 211 fortios_switch_controller_lldp_profile: 212 host: "{{ host }}" 213 username: "{{ username }}" 214 password: "{{ password }}" 215 vdom: "{{ vdom }}" 216 https: "False" 217 state: "present" 218 switch_controller_lldp_profile: 219 802.1_tlvs: "port-vlan-id" 220 802.3_tlvs: "max-frame-size" 221 auto_isl: "disable" 222 auto_isl_hello_timer: "6" 223 auto_isl_port_group: "7" 224 auto_isl_receive_timeout: "8" 225 custom_tlvs: 226 - 227 information_string: "<your_own_value>" 228 name: "default_name_11" 229 oui: "<your_own_value>" 230 subtype: "13" 231 med_network_policy: 232 - 233 dscp: "15" 234 name: "default_name_16" 235 priority: "17" 236 status: "disable" 237 vlan: "19" 238 med_tlvs: "inventory-management" 239 name: "default_name_21" 240''' 241 242RETURN = ''' 243build: 244 description: Build number of the fortigate image 245 returned: always 246 type: str 247 sample: '1547' 248http_method: 249 description: Last method used to provision the content into FortiGate 250 returned: always 251 type: str 252 sample: 'PUT' 253http_status: 254 description: Last result given by FortiGate on last operation applied 255 returned: always 256 type: str 257 sample: "200" 258mkey: 259 description: Master key (id) used in the last call to FortiGate 260 returned: success 261 type: str 262 sample: "id" 263name: 264 description: Name of the table used to fulfill the request 265 returned: always 266 type: str 267 sample: "urlfilter" 268path: 269 description: Path of the table used to fulfill the request 270 returned: always 271 type: str 272 sample: "webfilter" 273revision: 274 description: Internal revision number 275 returned: always 276 type: str 277 sample: "17.0.2.10658" 278serial: 279 description: Serial number of the unit 280 returned: always 281 type: str 282 sample: "FGVMEVYYQT3AB5352" 283status: 284 description: Indication of the operation's result 285 returned: always 286 type: str 287 sample: "success" 288vdom: 289 description: Virtual domain used 290 returned: always 291 type: str 292 sample: "root" 293version: 294 description: Version of the FortiGate 295 returned: always 296 type: str 297 sample: "v5.6.3" 298 299''' 300 301from ansible.module_utils.basic import AnsibleModule 302from ansible.module_utils.connection import Connection 303from ansible.module_utils.network.fortios.fortios import FortiOSHandler 304from ansible.module_utils.network.fortimanager.common import FAIL_SOCKET_MSG 305 306 307def login(data, fos): 308 host = data['host'] 309 username = data['username'] 310 password = data['password'] 311 ssl_verify = data['ssl_verify'] 312 313 fos.debug('on') 314 if 'https' in data and not data['https']: 315 fos.https('off') 316 else: 317 fos.https('on') 318 319 fos.login(host, username, password, verify=ssl_verify) 320 321 322def filter_switch_controller_lldp_profile_data(json): 323 option_list = ['802.1_tlvs', '802.3_tlvs', 'auto_isl', 324 'auto_isl_hello_timer', 'auto_isl_port_group', 'auto_isl_receive_timeout', 325 'custom_tlvs', 'med_network_policy', 'med_tlvs', 326 'name'] 327 dictionary = {} 328 329 for attribute in option_list: 330 if attribute in json and json[attribute] is not None: 331 dictionary[attribute] = json[attribute] 332 333 return dictionary 334 335 336def underscore_to_hyphen(data): 337 if isinstance(data, list): 338 for elem in data: 339 elem = underscore_to_hyphen(elem) 340 elif isinstance(data, dict): 341 new_data = {} 342 for k, v in data.items(): 343 new_data[k.replace('_', '-')] = underscore_to_hyphen(v) 344 data = new_data 345 346 return data 347 348 349def switch_controller_lldp_profile(data, fos): 350 vdom = data['vdom'] 351 if 'state' in data and data['state']: 352 state = data['state'] 353 elif 'state' in data['switch_controller_lldp_profile'] and data['switch_controller_lldp_profile']: 354 state = data['switch_controller_lldp_profile']['state'] 355 else: 356 state = True 357 switch_controller_lldp_profile_data = data['switch_controller_lldp_profile'] 358 filtered_data = underscore_to_hyphen(filter_switch_controller_lldp_profile_data(switch_controller_lldp_profile_data)) 359 360 if state == "present": 361 return fos.set('switch-controller', 362 'lldp-profile', 363 data=filtered_data, 364 vdom=vdom) 365 366 elif state == "absent": 367 return fos.delete('switch-controller', 368 'lldp-profile', 369 mkey=filtered_data['name'], 370 vdom=vdom) 371 372 373def is_successful_status(status): 374 return status['status'] == "success" or \ 375 status['http_method'] == "DELETE" and status['http_status'] == 404 376 377 378def fortios_switch_controller(data, fos): 379 380 if data['switch_controller_lldp_profile']: 381 resp = switch_controller_lldp_profile(data, fos) 382 383 return not is_successful_status(resp), \ 384 resp['status'] == "success", \ 385 resp 386 387 388def main(): 389 fields = { 390 "host": {"required": False, "type": "str"}, 391 "username": {"required": False, "type": "str"}, 392 "password": {"required": False, "type": "str", "default": "", "no_log": True}, 393 "vdom": {"required": False, "type": "str", "default": "root"}, 394 "https": {"required": False, "type": "bool", "default": True}, 395 "ssl_verify": {"required": False, "type": "bool", "default": True}, 396 "state": {"required": False, "type": "str", 397 "choices": ["present", "absent"]}, 398 "switch_controller_lldp_profile": { 399 "required": False, "type": "dict", "default": None, 400 "options": { 401 "state": {"required": False, "type": "str", 402 "choices": ["present", "absent"]}, 403 "802.1_tlvs": {"required": False, "type": "str", 404 "choices": ["port-vlan-id"]}, 405 "802.3_tlvs": {"required": False, "type": "str", 406 "choices": ["max-frame-size"]}, 407 "auto_isl": {"required": False, "type": "str", 408 "choices": ["disable", "enable"]}, 409 "auto_isl_hello_timer": {"required": False, "type": "int"}, 410 "auto_isl_port_group": {"required": False, "type": "int"}, 411 "auto_isl_receive_timeout": {"required": False, "type": "int"}, 412 "custom_tlvs": {"required": False, "type": "list", 413 "options": { 414 "information_string": {"required": False, "type": "str"}, 415 "name": {"required": True, "type": "str"}, 416 "oui": {"required": False, "type": "str"}, 417 "subtype": {"required": False, "type": "int"} 418 }}, 419 "med_network_policy": {"required": False, "type": "list", 420 "options": { 421 "dscp": {"required": False, "type": "int"}, 422 "name": {"required": True, "type": "str"}, 423 "priority": {"required": False, "type": "int"}, 424 "status": {"required": False, "type": "str", 425 "choices": ["disable", "enable"]}, 426 "vlan": {"required": False, "type": "int"} 427 }}, 428 "med_tlvs": {"required": False, "type": "str", 429 "choices": ["inventory-management", "network-policy"]}, 430 "name": {"required": True, "type": "str"} 431 432 } 433 } 434 } 435 436 module = AnsibleModule(argument_spec=fields, 437 supports_check_mode=False) 438 439 # legacy_mode refers to using fortiosapi instead of HTTPAPI 440 legacy_mode = 'host' in module.params and module.params['host'] is not None and \ 441 'username' in module.params and module.params['username'] is not None and \ 442 'password' in module.params and module.params['password'] is not None 443 444 if not legacy_mode: 445 if module._socket_path: 446 connection = Connection(module._socket_path) 447 fos = FortiOSHandler(connection) 448 449 is_error, has_changed, result = fortios_switch_controller(module.params, fos) 450 else: 451 module.fail_json(**FAIL_SOCKET_MSG) 452 else: 453 try: 454 from fortiosapi import FortiOSAPI 455 except ImportError: 456 module.fail_json(msg="fortiosapi module is required") 457 458 fos = FortiOSAPI() 459 460 login(module.params, fos) 461 is_error, has_changed, result = fortios_switch_controller(module.params, fos) 462 fos.logout() 463 464 if not is_error: 465 module.exit_json(changed=has_changed, meta=result) 466 else: 467 module.fail_json(msg="Error in repo", meta=result) 468 469 470if __name__ == '__main__': 471 main() 472