1#!/usr/bin/python 2# 3# This file is part of Ansible 4# 5# Ansible 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# Ansible 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 Ansible. If not, see <http://www.gnu.org/licenses/>. 17# 18 19ANSIBLE_METADATA = {'metadata_version': '1.1', 20 'status': ['preview'], 21 'supported_by': 'community'} 22 23DOCUMENTATION = ''' 24--- 25module: ce_mtu 26version_added: "2.4" 27short_description: Manages MTU settings on HUAWEI CloudEngine switches. 28description: 29 - Manages MTU settings on HUAWEI CloudEngine switches. 30author: QijunPan (@QijunPan) 31notes: 32 - Either C(sysmtu) param is required or C(interface) AND C(mtu) params are req'd. 33 - C(state=absent) unconfigures a given MTU if that value is currently present. 34 - Recommended connection is C(network_cli). 35 - This module also works with C(local) connections for legacy playbooks. 36options: 37 interface: 38 description: 39 - Full name of interface, i.e. 40GE1/0/22. 40 mtu: 41 description: 42 - MTU for a specific interface. 43 The value is an integer ranging from 46 to 9600, in bytes. 44 jumbo_max: 45 description: 46 - Maximum frame size. The default value is 9216. 47 The value is an integer and expressed in bytes. The value range is 1536 to 12224 for the CE12800 48 and 1536 to 12288 for ToR switches. 49 jumbo_min: 50 description: 51 - Non-jumbo frame size threshold. The default value is 1518. 52 The value is an integer that ranges from 1518 to jumbo_max, in bytes. 53 state: 54 description: 55 - Specify desired state of the resource. 56 default: present 57 choices: ['present','absent'] 58''' 59 60EXAMPLES = ''' 61- name: Mtu test 62 hosts: cloudengine 63 connection: local 64 gather_facts: no 65 vars: 66 cli: 67 host: "{{ inventory_hostname }}" 68 port: "{{ ansible_ssh_port }}" 69 username: "{{ username }}" 70 password: "{{ password }}" 71 transport: cli 72 73 tasks: 74 75 - name: "Config jumboframe on 40GE1/0/22" 76 ce_mtu: 77 interface: 40GE1/0/22 78 jumbo_max: 9000 79 jumbo_min: 8000 80 provider: "{{ cli }}" 81 82 - name: "Config mtu on 40GE1/0/22 (routed interface)" 83 ce_mtu: 84 interface: 40GE1/0/22 85 mtu: 1600 86 provider: "{{ cli }}" 87 88 - name: "Config mtu on 40GE1/0/23 (switched interface)" 89 ce_mtu: 90 interface: 40GE1/0/22 91 mtu: 9216 92 provider: "{{ cli }}" 93 94 - name: "Config mtu and jumboframe on 40GE1/0/22 (routed interface)" 95 ce_mtu: 96 interface: 40GE1/0/22 97 mtu: 1601 98 jumbo_max: 9001 99 jumbo_min: 8001 100 provider: "{{ cli }}" 101 102 - name: "Unconfigure mtu and jumboframe on a given interface" 103 ce_mtu: 104 state: absent 105 interface: 40GE1/0/22 106 provider: "{{ cli }}" 107''' 108 109RETURN = ''' 110proposed: 111 description: k/v pairs of parameters passed into module 112 returned: always 113 type: dict 114 sample: {"mtu": "1700", "jumbo_max": "9000", jumbo_min: "8000"} 115existing: 116 description: k/v pairs of existing mtu/sysmtu on the interface/system 117 returned: always 118 type: dict 119 sample: {"mtu": "1600", "jumbo_max": "9216", "jumbo_min": "1518"} 120end_state: 121 description: k/v pairs of mtu/sysmtu values after module execution 122 returned: always 123 type: dict 124 sample: {"mtu": "1700", "jumbo_max": "9000", jumbo_min: "8000"} 125updates: 126 description: command sent to the device 127 returned: always 128 type: list 129 sample: ["interface 40GE1/0/23", "mtu 1700", "jumboframe enable 9000 8000"] 130changed: 131 description: check to see if a change was made on the device 132 returned: always 133 type: bool 134 sample: true 135''' 136 137import re 138import copy 139from ansible.module_utils.basic import AnsibleModule 140from ansible.module_utils.network.cloudengine.ce import ce_argument_spec, load_config 141from ansible.module_utils.connection import exec_command 142 143 144def is_interface_support_setjumboframe(interface): 145 """is interface support set jumboframe""" 146 147 if interface is None: 148 return False 149 support_flag = False 150 if interface.upper().startswith('GE'): 151 support_flag = True 152 elif interface.upper().startswith('10GE'): 153 support_flag = True 154 elif interface.upper().startswith('25GE'): 155 support_flag = True 156 elif interface.upper().startswith('4X10GE'): 157 support_flag = True 158 elif interface.upper().startswith('40GE'): 159 support_flag = True 160 elif interface.upper().startswith('100GE'): 161 support_flag = True 162 else: 163 support_flag = False 164 return support_flag 165 166 167def get_interface_type(interface): 168 """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" 169 170 if interface is None: 171 return None 172 173 iftype = None 174 175 if interface.upper().startswith('GE'): 176 iftype = 'ge' 177 elif interface.upper().startswith('10GE'): 178 iftype = '10ge' 179 elif interface.upper().startswith('25GE'): 180 iftype = '25ge' 181 elif interface.upper().startswith('4X10GE'): 182 iftype = '4x10ge' 183 elif interface.upper().startswith('40GE'): 184 iftype = '40ge' 185 elif interface.upper().startswith('100GE'): 186 iftype = '100ge' 187 elif interface.upper().startswith('VLANIF'): 188 iftype = 'vlanif' 189 elif interface.upper().startswith('LOOPBACK'): 190 iftype = 'loopback' 191 elif interface.upper().startswith('METH'): 192 iftype = 'meth' 193 elif interface.upper().startswith('ETH-TRUNK'): 194 iftype = 'eth-trunk' 195 elif interface.upper().startswith('VBDIF'): 196 iftype = 'vbdif' 197 elif interface.upper().startswith('NVE'): 198 iftype = 'nve' 199 elif interface.upper().startswith('TUNNEL'): 200 iftype = 'tunnel' 201 elif interface.upper().startswith('ETHERNET'): 202 iftype = 'ethernet' 203 elif interface.upper().startswith('FCOE-PORT'): 204 iftype = 'fcoe-port' 205 elif interface.upper().startswith('FABRIC-PORT'): 206 iftype = 'fabric-port' 207 elif interface.upper().startswith('STACK-PORT'): 208 iftype = 'stack-Port' 209 elif interface.upper().startswith('NULL'): 210 iftype = 'null' 211 else: 212 return None 213 214 return iftype.lower() 215 216 217class Mtu(object): 218 """set mtu""" 219 220 def __init__(self, argument_spec): 221 self.spec = argument_spec 222 self.module = None 223 self.init_module() 224 225 # interface info 226 self.interface = self.module.params['interface'] 227 self.mtu = self.module.params['mtu'] 228 self.state = self.module.params['state'] 229 self.jbf_max = self.module.params['jumbo_max'] or None 230 self.jbf_min = self.module.params['jumbo_min'] or None 231 self.jbf_config = list() 232 self.jbf_cli = "" 233 self.commands = list() 234 235 # state 236 self.changed = False 237 self.updates_cmd = list() 238 self.results = dict() 239 self.proposed = dict() 240 self.existing = dict() 241 self.end_state = dict() 242 self.intf_info = dict() # one interface info 243 self.intf_type = None # loopback tunnel ... 244 245 def init_module(self): 246 """ init_module""" 247 248 self.module = AnsibleModule( 249 argument_spec=self.spec, supports_check_mode=True) 250 251 def get_config(self, flags=None): 252 """Retrieves the current config from the device or cache 253 """ 254 flags = [] if flags is None else flags 255 256 cmd = 'display current-configuration ' 257 cmd += ' '.join(flags) 258 cmd = cmd.strip() 259 260 rc, out, err = exec_command(self.module, cmd) 261 if rc != 0: 262 self.module.fail_json(msg=err) 263 cfg = str(out).strip() 264 265 return cfg 266 267 def get_interface_dict(self, ifname): 268 """ get one interface attributes dict.""" 269 intf_info = dict() 270 271 flags = list() 272 exp = r"| ignore-case section include ^#\s+interface %s\s+" % ifname.replace(" ", "") 273 flags.append(exp) 274 output = self.get_config(flags) 275 output_list = output.split('\n') 276 if output_list is None: 277 return intf_info 278 279 mtu = None 280 for config in output_list: 281 config = config.strip() 282 if config.startswith('mtu'): 283 mtu = re.findall(r'.*mtu\s*([0-9]*)', output)[0] 284 285 intf_info = dict(ifName=ifname, 286 ifMtu=mtu) 287 288 return intf_info 289 290 def prase_jumboframe_para(self, config_str): 291 """prase_jumboframe_para""" 292 293 interface_cli = "interface %s" % (self.interface.replace(" ", "").lower()) 294 if config_str.find(interface_cli) == -1: 295 self.module.fail_json(msg='Error: Interface does not exist.') 296 297 try: 298 npos1 = config_str.index('jumboframe enable') 299 except ValueError: 300 # return default vale 301 return [9216, 1518] 302 try: 303 npos2 = config_str.index('\n', npos1) 304 config_str_tmp = config_str[npos1:npos2] 305 except ValueError: 306 config_str_tmp = config_str[npos1:] 307 308 return re.findall(r'([0-9]+)', config_str_tmp) 309 310 def cli_load_config(self): 311 """load config by cli""" 312 313 if not self.module.check_mode: 314 if len(self.commands) > 1: 315 load_config(self.module, self.commands) 316 self.changed = True 317 318 def cli_add_command(self, command, undo=False): 319 """add command to self.update_cmd and self.commands""" 320 321 if undo and command.lower() not in ["quit", "return"]: 322 cmd = "undo " + command 323 else: 324 cmd = command 325 326 self.commands.append(cmd) # set to device 327 328 def get_jumboframe_config(self): 329 """ get_jumboframe_config""" 330 331 flags = list() 332 exp = r"| ignore-case section include ^#\s+interface %s\s+" % self.interface.replace(" ", "") 333 flags.append(exp) 334 output = self.get_config(flags) 335 output = output.replace('*', '').lower() 336 337 return self.prase_jumboframe_para(output) 338 339 def set_jumboframe(self): 340 """ set_jumboframe""" 341 342 if self.state == "present": 343 if not self.jbf_max and not self.jbf_min: 344 return 345 346 jbf_value = self.get_jumboframe_config() 347 self.jbf_config = copy.deepcopy(jbf_value) 348 if len(jbf_value) == 1: 349 jbf_value.append("1518") 350 self.jbf_config.append("1518") 351 if not self.jbf_max: 352 return 353 354 if (len(jbf_value) > 2) or (len(jbf_value) == 0): 355 self.module.fail_json( 356 msg='Error: Get jubmoframe config value num error.') 357 if self.jbf_min is None: 358 if jbf_value[0] == self.jbf_max: 359 return 360 else: 361 if (jbf_value[0] == self.jbf_max) \ 362 and (jbf_value[1] == self.jbf_min): 363 return 364 if jbf_value[0] != self.jbf_max: 365 jbf_value[0] = self.jbf_max 366 if (jbf_value[1] != self.jbf_min) and (self.jbf_min is not None): 367 jbf_value[1] = self.jbf_min 368 else: 369 jbf_value.pop(1) 370 else: 371 jbf_value = self.get_jumboframe_config() 372 self.jbf_config = copy.deepcopy(jbf_value) 373 if (jbf_value == [9216, 1518]): 374 return 375 jbf_value = [9216, 1518] 376 377 if len(jbf_value) == 2: 378 self.jbf_cli = "jumboframe enable %s %s" % ( 379 jbf_value[0], jbf_value[1]) 380 else: 381 self.jbf_cli = "jumboframe enable %s" % (jbf_value[0]) 382 self.cli_add_command(self.jbf_cli) 383 384 if self.state == "present": 385 if self.jbf_min: 386 self.updates_cmd.append( 387 "jumboframe enable %s %s" % (self.jbf_max, self.jbf_min)) 388 else: 389 self.updates_cmd.append("jumboframe enable %s" % (self.jbf_max)) 390 else: 391 self.updates_cmd.append("undo jumboframe enable") 392 393 return 394 395 def merge_interface(self, ifname, mtu): 396 """ Merge interface mtu.""" 397 398 xmlstr = '' 399 change = False 400 401 command = "interface %s" % ifname 402 self.cli_add_command(command) 403 404 if self.state == "present": 405 if mtu and self.intf_info["ifMtu"] != mtu: 406 command = "mtu %s" % mtu 407 self.cli_add_command(command) 408 self.updates_cmd.append("mtu %s" % mtu) 409 change = True 410 else: 411 if self.intf_info["ifMtu"] != '1500' and self.intf_info["ifMtu"]: 412 command = "mtu 1500" 413 self.cli_add_command(command) 414 self.updates_cmd.append("undo mtu") 415 change = True 416 417 return 418 419 def check_params(self): 420 """Check all input params""" 421 422 # interface type check 423 if self.interface: 424 self.intf_type = get_interface_type(self.interface) 425 if not self.intf_type: 426 self.module.fail_json( 427 msg='Error: Interface name of %s ' 428 'is error.' % self.interface) 429 430 if not self.intf_type: 431 self.module.fail_json( 432 msg='Error: Interface %s is error.') 433 434 # mtu check mtu 435 if self.mtu: 436 if not self.mtu.isdigit(): 437 self.module.fail_json(msg='Error: Mtu is invalid.') 438 # check mtu range 439 if int(self.mtu) < 46 or int(self.mtu) > 9600: 440 self.module.fail_json( 441 msg='Error: Mtu is not in the range from 46 to 9600.') 442 # get interface info 443 self.intf_info = self.get_interface_dict(self.interface) 444 if not self.intf_info: 445 self.module.fail_json(msg='Error: interface does not exist.') 446 447 # check interface can set jumbo frame 448 if self.state == 'present': 449 if self.jbf_max: 450 if not is_interface_support_setjumboframe(self.interface): 451 self.module.fail_json( 452 msg='Error: Interface %s does not support jumboframe set.' % self.interface) 453 if not self.jbf_max.isdigit(): 454 self.module.fail_json( 455 msg='Error: Max jumboframe is not digit.') 456 if (int(self.jbf_max) > 12288) or (int(self.jbf_max) < 1536): 457 self.module.fail_json( 458 msg='Error: Max jumboframe is between 1536 to 12288.') 459 460 if self.jbf_min: 461 if not self.jbf_min.isdigit(): 462 self.module.fail_json( 463 msg='Error: Min jumboframe is not digit.') 464 if not self.jbf_max: 465 self.module.fail_json( 466 msg='Error: please specify max jumboframe value.') 467 if (int(self.jbf_min) > int(self.jbf_max)) or (int(self.jbf_min) < 1518): 468 self.module.fail_json( 469 msg='Error: Min jumboframe is between ' 470 '1518 to jumboframe max value.') 471 472 if self.jbf_min is not None: 473 if self.jbf_max is None: 474 self.module.fail_json( 475 msg='Error: please input MAX jumboframe ' 476 'value.') 477 478 def get_proposed(self): 479 """ get_proposed""" 480 481 self.proposed['state'] = self.state 482 if self.interface: 483 self.proposed["interface"] = self.interface 484 485 if self.state == 'present': 486 if self.mtu: 487 self.proposed["mtu"] = self.mtu 488 if self.jbf_max: 489 if self.jbf_min: 490 self.proposed["jumboframe"] = "jumboframe enable %s %s" % ( 491 self.jbf_max, self.jbf_min) 492 else: 493 self.proposed[ 494 "jumboframe"] = "jumboframe enable %s %s" % (self.jbf_max, 1518) 495 496 def get_existing(self): 497 """ get_existing""" 498 499 if self.intf_info: 500 self.existing["interface"] = self.intf_info["ifName"] 501 self.existing["mtu"] = self.intf_info["ifMtu"] 502 503 if self.intf_info: 504 if not self.existing["interface"]: 505 self.existing["interface"] = self.interface 506 507 if len(self.jbf_config) != 2: 508 return 509 510 self.existing["jumboframe"] = "jumboframe enable %s %s" % ( 511 self.jbf_config[0], self.jbf_config[1]) 512 513 def get_end_state(self): 514 """ get_end_state""" 515 516 if self.intf_info: 517 end_info = self.get_interface_dict(self.interface) 518 if end_info: 519 self.end_state["interface"] = end_info["ifName"] 520 self.end_state["mtu"] = end_info["ifMtu"] 521 if self.intf_info: 522 if not self.end_state["interface"]: 523 self.end_state["interface"] = self.interface 524 525 if self.state == 'absent': 526 self.end_state["jumboframe"] = "jumboframe enable %s %s" % ( 527 9216, 1518) 528 elif not self.jbf_max and not self.jbf_min: 529 if len(self.jbf_config) != 2: 530 return 531 self.end_state["jumboframe"] = "jumboframe enable %s %s" % ( 532 self.jbf_config[0], self.jbf_config[1]) 533 elif self.jbf_min: 534 self.end_state["jumboframe"] = "jumboframe enable %s %s" % ( 535 self.jbf_max, self.jbf_min) 536 else: 537 self.end_state[ 538 "jumboframe"] = "jumboframe enable %s %s" % (self.jbf_max, 1518) 539 if self.end_state == self.existing: 540 self.changed = False 541 542 def work(self): 543 """worker""" 544 self.check_params() 545 546 self.get_proposed() 547 548 self.merge_interface(self.interface, self.mtu) 549 self.set_jumboframe() 550 self.cli_load_config() 551 552 self.get_existing() 553 self.get_end_state() 554 self.results['changed'] = self.changed 555 self.results['proposed'] = self.proposed 556 self.results['existing'] = self.existing 557 self.results['end_state'] = self.end_state 558 if self.changed: 559 self.results['updates'] = self.updates_cmd 560 else: 561 self.results['updates'] = list() 562 563 self.module.exit_json(**self.results) 564 565 566def main(): 567 """ main""" 568 569 argument_spec = dict( 570 interface=dict(required=True, type='str'), 571 mtu=dict(type='str'), 572 state=dict(choices=['absent', 'present'], 573 default='present', required=False), 574 jumbo_max=dict(type='str'), 575 jumbo_min=dict(type='str'), 576 ) 577 argument_spec.update(ce_argument_spec) 578 interface = Mtu(argument_spec) 579 interface.work() 580 581 582if __name__ == '__main__': 583 main() 584