#!/usr/bin/python # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . # from __future__ import (absolute_import, division, print_function) __metaclass__ = type ANSIBLE_METADATA = {'metadata_version': '1.1', 'status': ['preview'], 'supported_by': 'community'} DOCUMENTATION = ''' --- module: ce_mtu short_description: Manages MTU settings on HUAWEI CloudEngine switches. description: - Manages MTU settings on HUAWEI CloudEngine switches. author: QijunPan (@QijunPan) notes: - Either C(sysmtu) param is required or C(interface) AND C(mtu) params are req'd. - C(state=absent) unconfigures a given MTU if that value is currently present. - Recommended connection is C(network_cli). - This module also works with C(local) connections for legacy playbooks. options: interface: description: - Full name of interface, i.e. 40GE1/0/22. mtu: description: - MTU for a specific interface. The value is an integer ranging from 46 to 9600, in bytes. jumbo_max: description: - Maximum frame size. The default value is 9216. The value is an integer and expressed in bytes. The value range is 1536 to 12224 for the CE12800 and 1536 to 12288 for ToR switches. jumbo_min: description: - Non-jumbo frame size threshold. The default value is 1518. The value is an integer that ranges from 1518 to jumbo_max, in bytes. state: description: - Specify desired state of the resource. default: present choices: ['present','absent'] ''' EXAMPLES = ''' - name: Mtu test hosts: cloudengine connection: local gather_facts: no vars: cli: host: "{{ inventory_hostname }}" port: "{{ ansible_ssh_port }}" username: "{{ username }}" password: "{{ password }}" transport: cli tasks: - name: "Config jumboframe on 40GE1/0/22" ce_mtu: interface: 40GE1/0/22 jumbo_max: 9000 jumbo_min: 8000 provider: "{{ cli }}" - name: "Config mtu on 40GE1/0/22 (routed interface)" ce_mtu: interface: 40GE1/0/22 mtu: 1600 provider: "{{ cli }}" - name: "Config mtu on 40GE1/0/23 (switched interface)" ce_mtu: interface: 40GE1/0/22 mtu: 9216 provider: "{{ cli }}" - name: "Config mtu and jumboframe on 40GE1/0/22 (routed interface)" ce_mtu: interface: 40GE1/0/22 mtu: 1601 jumbo_max: 9001 jumbo_min: 8001 provider: "{{ cli }}" - name: "Unconfigure mtu and jumboframe on a given interface" ce_mtu: state: absent interface: 40GE1/0/22 provider: "{{ cli }}" ''' RETURN = ''' proposed: description: k/v pairs of parameters passed into module returned: always type: dict sample: {"mtu": "1700", "jumbo_max": "9000", jumbo_min: "8000"} existing: description: k/v pairs of existing mtu/sysmtu on the interface/system returned: always type: dict sample: {"mtu": "1600", "jumbo_max": "9216", "jumbo_min": "1518"} end_state: description: k/v pairs of mtu/sysmtu values after module execution returned: always type: dict sample: {"mtu": "1700", "jumbo_max": "9000", jumbo_min: "8000"} updates: description: command sent to the device returned: always type: list sample: ["interface 40GE1/0/23", "mtu 1700", "jumboframe enable 9000 8000"] changed: description: check to see if a change was made on the device returned: always type: bool sample: true ''' import re import copy from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.general.plugins.module_utils.network.cloudengine.ce import ce_argument_spec, load_config from ansible.module_utils.connection import exec_command def is_interface_support_setjumboframe(interface): """is interface support set jumboframe""" if interface is None: return False support_flag = False if interface.upper().startswith('GE'): support_flag = True elif interface.upper().startswith('10GE'): support_flag = True elif interface.upper().startswith('25GE'): support_flag = True elif interface.upper().startswith('4X10GE'): support_flag = True elif interface.upper().startswith('40GE'): support_flag = True elif interface.upper().startswith('100GE'): support_flag = True else: support_flag = False return support_flag def get_interface_type(interface): """Gets the type of interface, such as 10GE, ETH-TRUNK, VLANIF...""" if interface is None: return None iftype = None if interface.upper().startswith('GE'): iftype = 'ge' elif interface.upper().startswith('10GE'): iftype = '10ge' elif interface.upper().startswith('25GE'): iftype = '25ge' elif interface.upper().startswith('4X10GE'): iftype = '4x10ge' elif interface.upper().startswith('40GE'): iftype = '40ge' elif interface.upper().startswith('100GE'): iftype = '100ge' elif interface.upper().startswith('VLANIF'): iftype = 'vlanif' elif interface.upper().startswith('LOOPBACK'): iftype = 'loopback' elif interface.upper().startswith('METH'): iftype = 'meth' elif interface.upper().startswith('ETH-TRUNK'): iftype = 'eth-trunk' elif interface.upper().startswith('VBDIF'): iftype = 'vbdif' elif interface.upper().startswith('NVE'): iftype = 'nve' elif interface.upper().startswith('TUNNEL'): iftype = 'tunnel' elif interface.upper().startswith('ETHERNET'): iftype = 'ethernet' elif interface.upper().startswith('FCOE-PORT'): iftype = 'fcoe-port' elif interface.upper().startswith('FABRIC-PORT'): iftype = 'fabric-port' elif interface.upper().startswith('STACK-PORT'): iftype = 'stack-Port' elif interface.upper().startswith('NULL'): iftype = 'null' else: return None return iftype.lower() class Mtu(object): """set mtu""" def __init__(self, argument_spec): self.spec = argument_spec self.module = None self.init_module() # interface info self.interface = self.module.params['interface'] self.mtu = self.module.params['mtu'] self.state = self.module.params['state'] self.jbf_max = self.module.params['jumbo_max'] or None self.jbf_min = self.module.params['jumbo_min'] or None self.jbf_config = list() self.jbf_cli = "" self.commands = list() # state self.changed = False self.updates_cmd = list() self.results = dict() self.proposed = dict() self.existing = dict() self.end_state = dict() self.intf_info = dict() # one interface info self.intf_type = None # loopback tunnel ... def init_module(self): """ init_module""" self.module = AnsibleModule( argument_spec=self.spec, supports_check_mode=True) def get_config(self, flags=None): """Retrieves the current config from the device or cache """ flags = [] if flags is None else flags cmd = 'display current-configuration ' cmd += ' '.join(flags) cmd = cmd.strip() rc, out, err = exec_command(self.module, cmd) if rc != 0: self.module.fail_json(msg=err) cfg = str(out).strip() return cfg def get_interface_dict(self, ifname): """ get one interface attributes dict.""" intf_info = dict() flags = list() exp = r"| ignore-case section include ^#\s+interface %s\s+" % ifname.replace(" ", "") flags.append(exp) output = self.get_config(flags) output_list = output.split('\n') if output_list is None: return intf_info mtu = None for config in output_list: config = config.strip() if config.startswith('mtu'): mtu = re.findall(r'.*mtu\s*([0-9]*)', output)[0] intf_info = dict(ifName=ifname, ifMtu=mtu) return intf_info def prase_jumboframe_para(self, config_str): """prase_jumboframe_para""" interface_cli = "interface %s" % (self.interface.replace(" ", "").lower()) if config_str.find(interface_cli) == -1: self.module.fail_json(msg='Error: Interface does not exist.') try: npos1 = config_str.index('jumboframe enable') except ValueError: # return default vale return [9216, 1518] try: npos2 = config_str.index('\n', npos1) config_str_tmp = config_str[npos1:npos2] except ValueError: config_str_tmp = config_str[npos1:] return re.findall(r'([0-9]+)', config_str_tmp) def cli_load_config(self): """load config by cli""" if not self.module.check_mode: if len(self.commands) > 1: load_config(self.module, self.commands) self.changed = True def cli_add_command(self, command, undo=False): """add command to self.update_cmd and self.commands""" if undo and command.lower() not in ["quit", "return"]: cmd = "undo " + command else: cmd = command self.commands.append(cmd) # set to device def get_jumboframe_config(self): """ get_jumboframe_config""" flags = list() exp = r"| ignore-case section include ^#\s+interface %s\s+" % self.interface.replace(" ", "") flags.append(exp) output = self.get_config(flags) output = output.replace('*', '').lower() return self.prase_jumboframe_para(output) def set_jumboframe(self): """ set_jumboframe""" if self.state == "present": if not self.jbf_max and not self.jbf_min: return jbf_value = self.get_jumboframe_config() self.jbf_config = copy.deepcopy(jbf_value) if len(jbf_value) == 1: jbf_value.append("1518") self.jbf_config.append("1518") if not self.jbf_max: return if (len(jbf_value) > 2) or (len(jbf_value) == 0): self.module.fail_json( msg='Error: Get jubmoframe config value num error.') if self.jbf_min is None: if jbf_value[0] == self.jbf_max: return else: if (jbf_value[0] == self.jbf_max) \ and (jbf_value[1] == self.jbf_min): return if jbf_value[0] != self.jbf_max: jbf_value[0] = self.jbf_max if (jbf_value[1] != self.jbf_min) and (self.jbf_min is not None): jbf_value[1] = self.jbf_min else: jbf_value.pop(1) else: jbf_value = self.get_jumboframe_config() self.jbf_config = copy.deepcopy(jbf_value) if (jbf_value == [9216, 1518]): return jbf_value = [9216, 1518] if len(jbf_value) == 2: self.jbf_cli = "jumboframe enable %s %s" % ( jbf_value[0], jbf_value[1]) else: self.jbf_cli = "jumboframe enable %s" % (jbf_value[0]) self.cli_add_command(self.jbf_cli) if self.state == "present": if self.jbf_min: self.updates_cmd.append( "jumboframe enable %s %s" % (self.jbf_max, self.jbf_min)) else: self.updates_cmd.append("jumboframe enable %s" % (self.jbf_max)) else: self.updates_cmd.append("undo jumboframe enable") return def merge_interface(self, ifname, mtu): """ Merge interface mtu.""" xmlstr = '' change = False command = "interface %s" % ifname self.cli_add_command(command) if self.state == "present": if mtu and self.intf_info["ifMtu"] != mtu: command = "mtu %s" % mtu self.cli_add_command(command) self.updates_cmd.append("mtu %s" % mtu) change = True else: if self.intf_info["ifMtu"] != '1500' and self.intf_info["ifMtu"]: command = "mtu 1500" self.cli_add_command(command) self.updates_cmd.append("undo mtu") change = True return def check_params(self): """Check all input params""" # interface type check if self.interface: self.intf_type = get_interface_type(self.interface) if not self.intf_type: self.module.fail_json( msg='Error: Interface name of %s ' 'is error.' % self.interface) if not self.intf_type: self.module.fail_json( msg='Error: Interface %s is error.') # mtu check mtu if self.mtu: if not self.mtu.isdigit(): self.module.fail_json(msg='Error: Mtu is invalid.') # check mtu range if int(self.mtu) < 46 or int(self.mtu) > 9600: self.module.fail_json( msg='Error: Mtu is not in the range from 46 to 9600.') # get interface info self.intf_info = self.get_interface_dict(self.interface) if not self.intf_info: self.module.fail_json(msg='Error: interface does not exist.') # check interface can set jumbo frame if self.state == 'present': if self.jbf_max: if not is_interface_support_setjumboframe(self.interface): self.module.fail_json( msg='Error: Interface %s does not support jumboframe set.' % self.interface) if not self.jbf_max.isdigit(): self.module.fail_json( msg='Error: Max jumboframe is not digit.') if (int(self.jbf_max) > 12288) or (int(self.jbf_max) < 1536): self.module.fail_json( msg='Error: Max jumboframe is between 1536 to 12288.') if self.jbf_min: if not self.jbf_min.isdigit(): self.module.fail_json( msg='Error: Min jumboframe is not digit.') if not self.jbf_max: self.module.fail_json( msg='Error: please specify max jumboframe value.') if (int(self.jbf_min) > int(self.jbf_max)) or (int(self.jbf_min) < 1518): self.module.fail_json( msg='Error: Min jumboframe is between ' '1518 to jumboframe max value.') if self.jbf_min is not None: if self.jbf_max is None: self.module.fail_json( msg='Error: please input MAX jumboframe ' 'value.') def get_proposed(self): """ get_proposed""" self.proposed['state'] = self.state if self.interface: self.proposed["interface"] = self.interface if self.state == 'present': if self.mtu: self.proposed["mtu"] = self.mtu if self.jbf_max: if self.jbf_min: self.proposed["jumboframe"] = "jumboframe enable %s %s" % ( self.jbf_max, self.jbf_min) else: self.proposed[ "jumboframe"] = "jumboframe enable %s %s" % (self.jbf_max, 1518) def get_existing(self): """ get_existing""" if self.intf_info: self.existing["interface"] = self.intf_info["ifName"] self.existing["mtu"] = self.intf_info["ifMtu"] if self.intf_info: if not self.existing["interface"]: self.existing["interface"] = self.interface if len(self.jbf_config) != 2: return self.existing["jumboframe"] = "jumboframe enable %s %s" % ( self.jbf_config[0], self.jbf_config[1]) def get_end_state(self): """ get_end_state""" if self.intf_info: end_info = self.get_interface_dict(self.interface) if end_info: self.end_state["interface"] = end_info["ifName"] self.end_state["mtu"] = end_info["ifMtu"] if self.intf_info: if not self.end_state["interface"]: self.end_state["interface"] = self.interface if self.state == 'absent': self.end_state["jumboframe"] = "jumboframe enable %s %s" % ( 9216, 1518) elif not self.jbf_max and not self.jbf_min: if len(self.jbf_config) != 2: return self.end_state["jumboframe"] = "jumboframe enable %s %s" % ( self.jbf_config[0], self.jbf_config[1]) elif self.jbf_min: self.end_state["jumboframe"] = "jumboframe enable %s %s" % ( self.jbf_max, self.jbf_min) else: self.end_state[ "jumboframe"] = "jumboframe enable %s %s" % (self.jbf_max, 1518) if self.end_state == self.existing: self.changed = False def work(self): """worker""" self.check_params() self.get_proposed() self.merge_interface(self.interface, self.mtu) self.set_jumboframe() self.cli_load_config() self.get_existing() self.get_end_state() self.results['changed'] = self.changed self.results['proposed'] = self.proposed self.results['existing'] = self.existing self.results['end_state'] = self.end_state if self.changed: self.results['updates'] = self.updates_cmd else: self.results['updates'] = list() self.module.exit_json(**self.results) def main(): """ main""" argument_spec = dict( interface=dict(required=True, type='str'), mtu=dict(type='str'), state=dict(choices=['absent', 'present'], default='present', required=False), jumbo_max=dict(type='str'), jumbo_min=dict(type='str'), ) argument_spec.update(ce_argument_spec) interface = Mtu(argument_spec) interface.work() if __name__ == '__main__': main()