Merge "os-net-config runs ethtool command without restarting interfaces" into stable/wallaby
This commit is contained in:
		
							
								
								
									
										5
									
								
								bindep.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								bindep.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| # This is a cross-platform list tracking distribution packages needed for | ||||
| # install and tests; | ||||
| # see https://docs.openstack.org/infra/bindep/ for additional information. | ||||
|  | ||||
| ethtool | ||||
| @@ -261,7 +261,8 @@ class IfcfgNetConfig(os_net_config.NetConfig): | ||||
|             "IPADDR", | ||||
|             "NETMASK", | ||||
|             "MTU", | ||||
|             "ONBOOT" | ||||
|             "ONBOOT", | ||||
|             "ETHTOOL_OPTS" | ||||
|         ] | ||||
|         # Check whether any of the changes require restart | ||||
|         for change in self.enumerate_ifcfg_changes(file_values, new_values): | ||||
| @@ -331,6 +332,46 @@ class IfcfgNetConfig(os_net_config.NetConfig): | ||||
|                 commands.append("link set dev %s mtu 1500" % device_name) | ||||
|         return commands | ||||
|  | ||||
|     def ethtool_apply_command(self, device_name, filename, data): | ||||
|         """Return list of commands needed to implement changes. | ||||
|  | ||||
|            Given ifcfg data for an interface, return commands required to | ||||
|            apply the configuration using 'ethtool' commands. | ||||
|  | ||||
|         :param device_name: The name of the int, bridge, or bond | ||||
|         :type device_name: string | ||||
|         :param filename: The ifcfg-<int> filename. | ||||
|         :type filename: string | ||||
|         :param data: The data for the new ifcfg-<int> file. | ||||
|         :type data: string | ||||
|         :returns: commands (commands to be run) | ||||
|         """ | ||||
|  | ||||
|         previous_cfg = common.get_file_data(filename) | ||||
|         file_values = self.parse_ifcfg(previous_cfg) | ||||
|         data_values = self.parse_ifcfg(data) | ||||
|         logger.debug("File values:\n%s" % file_values) | ||||
|         logger.debug("Data values:\n%s" % data_values) | ||||
|         changes = self.enumerate_ifcfg_changes(file_values, data_values) | ||||
|         commands = [] | ||||
|  | ||||
|         if "ETHTOOL_OPTS" in changes: | ||||
|             if changes["ETHTOOL_OPTS"] == "added" or \ | ||||
|                changes["ETHTOOL_OPTS"] == "modified": | ||||
|                 for command_opts in data_values["ETHTOOL_OPTS"].split(';'): | ||||
|                     if re.match(r'\s*-+\w+-*\w* ', command_opts): | ||||
|                         if device_name or "${DEVICE}" or "$DEVICE" \ | ||||
|                                 in command_opts: | ||||
|                             commands.append("%s" % command_opts) | ||||
|                         else: | ||||
|                             msg = ("Assigned interface name to \ | ||||
|                                     ETHTOOL_OPTS is invalid %s" % device_name) | ||||
|                             raise utils.InvalidInterfaceException(msg) | ||||
|                     else: | ||||
|                         commands.append("-s %s %s" % | ||||
|                                         (device_name, command_opts)) | ||||
|         return commands | ||||
|  | ||||
|     def iproute2_route_commands(self, filename, data): | ||||
|         """Return a list of commands for 'ip route' to modify routing table. | ||||
|  | ||||
| @@ -1292,6 +1333,7 @@ class IfcfgNetConfig(os_net_config.NetConfig): | ||||
|         vpp_interfaces = self.vpp_interface_data.values() | ||||
|         vpp_bonds = self.vpp_bond_data.values() | ||||
|         ipcmd = utils.iproute2_path() | ||||
|         ethtoolcmd = utils.ethtool_path() | ||||
|  | ||||
|         for interface_name, iface_data in self.interface_data.items(): | ||||
|             route_data = self.route_data.get(interface_name, '') | ||||
| @@ -1736,6 +1778,27 @@ class IfcfgNetConfig(os_net_config.NetConfig): | ||||
|                             self.child_members(interface[0])) | ||||
|                         break | ||||
|  | ||||
|                 commands = self.ethtool_apply_command(interface[0], | ||||
|                                                       interface[1], | ||||
|                                                       interface[2]) | ||||
|                 if commands is not None: | ||||
|                     for command in commands: | ||||
|                         try: | ||||
|                             args = command.split() | ||||
|                             args = [interface[0] | ||||
|                                     if item in ["${DEVICE}", "$DEVICE"] | ||||
|                                     else item for item in args] | ||||
|                             self.execute('Running ethtool %s' % command, | ||||
|                                          ethtoolcmd, *args) | ||||
|                         except Exception as e: | ||||
|                             logger.warning("Error in 'ethtool %s', restarting %s:\ | ||||
|                                            \n%s)" % | ||||
|                                            (command, interface[0], str(e))) | ||||
|                             restart_interfaces.append(interface[0]) | ||||
|                             restart_interfaces.extend( | ||||
|                                 self.child_members(interface[0])) | ||||
|                             break | ||||
|  | ||||
|             for bridge in apply_bridges: | ||||
|                 logger.debug('Running ip commands on bridge: %s' % | ||||
|                              bridge[0]) | ||||
|   | ||||
| @@ -104,6 +104,7 @@ BOOTPROTO="dhcp" | ||||
| ONBOOT="yes" | ||||
| TYPE="Ethernet" | ||||
| """ | ||||
|  | ||||
| _IFCFG_STATIC1 = """# This file is autogenerated by os-net-config | ||||
| DEVICE=eth0 | ||||
| BOOTPROTO=static | ||||
| @@ -115,6 +116,31 @@ ONBOOT=yes | ||||
|  | ||||
| _IFCFG_STATIC1_MTU = _IFCFG_STATIC1 + "\nMTU=9000" | ||||
|  | ||||
| _IFCFG_STATIC1_ETHTOOL1 = _IFCFG_STATIC1 +\ | ||||
|     "\nETHTOOL_OPTS=\"speed 100 duplex full autoneg off\"" | ||||
|  | ||||
| _IFCFG_STATIC1_ETHTOOL2 = _IFCFG_STATIC1 +\ | ||||
|     "\nETHTOOL_OPTS=\"-G ${DEVICE} rx 1024 tx 1024\"" | ||||
|  | ||||
| _IFCFG_STATIC1_ETHTOOL3 = _IFCFG_STATIC1 +\ | ||||
|     "\nETHTOOL_OPTS=\"--set-ring ${DEVICE} rx 1024 tx 1024\"" | ||||
|  | ||||
| _IFCFG_STATIC1_ETHTOOL4 = _IFCFG_STATIC1 +\ | ||||
|     "\nETHTOOL_OPTS=\"--pause ${DEVICE} autoneg on\"" | ||||
|  | ||||
| _IFCFG_STATIC1_ETHTOOL5 = _IFCFG_STATIC1 +\ | ||||
|     "\nETHTOOL_OPTS=\"-G ${DEVICE} rx 1024 tx 1024;\ | ||||
| -A ${DEVICE} autoneg on;\ | ||||
| --pause ${DEVICE} autoneg off;\ | ||||
| --set-ring ${DEVICE} rx 512\"" | ||||
|  | ||||
| _IFCFG_STATIC1_ETHTOOL6 = _IFCFG_STATIC1 +\ | ||||
|     "\nETHTOOL_OPTS=\"-G $DEVICE rx 1024 tx 1024\"" | ||||
|  | ||||
| _IFCFG_STATIC1_ETHTOOL7 = _IFCFG_STATIC1 +\ | ||||
|     "\nETHTOOL_OPTS=\"-G eth0 rx 1024 tx 1024\"" | ||||
|  | ||||
|  | ||||
| _IFCFG_STATIC2 = """DEVICE=eth0 | ||||
| BOOTPROTO=static | ||||
| IPADDR=10.0.1.2 | ||||
| @@ -2445,6 +2471,8 @@ class TestIfcfgNetConfigApply(base.TestCase): | ||||
|                                                          _IFCFG_ROUTES2) | ||||
|         self.assertTrue(commands == command_list1) | ||||
|  | ||||
|         shutil.rmtree(tmpdir) | ||||
|  | ||||
|     def test_ifcfg_rule_commands(self): | ||||
|  | ||||
|         tmpdir = tempfile.mkdtemp() | ||||
| @@ -2463,6 +2491,8 @@ class TestIfcfgNetConfigApply(base.TestCase): | ||||
|                                                         _IFCFG_RULES2) | ||||
|         self.assertTrue(commands == command_list1) | ||||
|  | ||||
|         shutil.rmtree(tmpdir) | ||||
|  | ||||
|     def test_ifcfg_ipmtu_commands(self): | ||||
|  | ||||
|         tmpdir = tempfile.mkdtemp() | ||||
| @@ -2496,6 +2526,64 @@ class TestIfcfgNetConfigApply(base.TestCase): | ||||
|                                                          _IFCFG_STATIC2_MTU) | ||||
|         self.assertTrue(commands == command_list3) | ||||
|  | ||||
|         shutil.rmtree(tmpdir) | ||||
|  | ||||
|     def test_ifcfg_ethtool_commands(self): | ||||
|  | ||||
|         tmpdir = tempfile.mkdtemp() | ||||
|         interface = "eth0" | ||||
|         interface_filename = tmpdir + '/ifcfg-' + interface | ||||
|         file = open(interface_filename, 'w') | ||||
|         file.write(_IFCFG_STATIC1) | ||||
|         file.close() | ||||
|  | ||||
|         command_list1 = ['-s eth0 speed 100 duplex full autoneg off'] | ||||
|         command = self.provider.ethtool_apply_command(interface, | ||||
|                                                       interface_filename, | ||||
|                                                       _IFCFG_STATIC1_ETHTOOL1) | ||||
|         self.assertTrue(command == command_list1) | ||||
|  | ||||
|         command_list2 = ['-G ${DEVICE} rx 1024 tx 1024'] | ||||
|         command = self.provider.ethtool_apply_command(interface, | ||||
|                                                       interface_filename, | ||||
|                                                       _IFCFG_STATIC1_ETHTOOL2) | ||||
|         self.assertTrue(command == command_list2) | ||||
|  | ||||
|         command_list3 = ['--set-ring ${DEVICE} rx 1024 tx 1024'] | ||||
|         command = self.provider.ethtool_apply_command(interface, | ||||
|                                                       interface_filename, | ||||
|                                                       _IFCFG_STATIC1_ETHTOOL3) | ||||
|         self.assertTrue(command == command_list3) | ||||
|  | ||||
|         command_list4 = ['--pause ${DEVICE} autoneg on'] | ||||
|         command = self.provider.ethtool_apply_command(interface, | ||||
|                                                       interface_filename, | ||||
|                                                       _IFCFG_STATIC1_ETHTOOL4) | ||||
|         self.assertTrue(command == command_list4) | ||||
|  | ||||
|         command_list5 = ['-G ${DEVICE} rx 1024 tx 1024', | ||||
|                          '-A ${DEVICE} autoneg on', | ||||
|                          '--pause ${DEVICE} autoneg off', | ||||
|                          '--set-ring ${DEVICE} rx 512'] | ||||
|         command = self.provider.ethtool_apply_command(interface, | ||||
|                                                       interface_filename, | ||||
|                                                       _IFCFG_STATIC1_ETHTOOL5) | ||||
|         self.assertTrue(command == command_list5) | ||||
|  | ||||
|         command_list6 = ['-G $DEVICE rx 1024 tx 1024'] | ||||
|         command = self.provider.ethtool_apply_command(interface, | ||||
|                                                       interface_filename, | ||||
|                                                       _IFCFG_STATIC1_ETHTOOL6) | ||||
|         self.assertTrue(command == command_list6) | ||||
|  | ||||
|         command_list7 = ['-G eth0 rx 1024 tx 1024'] | ||||
|         command = self.provider.ethtool_apply_command(interface, | ||||
|                                                       interface_filename, | ||||
|                                                       _IFCFG_STATIC1_ETHTOOL7) | ||||
|         self.assertTrue(command == command_list7) | ||||
|  | ||||
|         shutil.rmtree(tmpdir) | ||||
|  | ||||
|     def test_restart_children_on_change(self): | ||||
|         # setup and apply a bridge | ||||
|         interface = objects.Interface('em1') | ||||
|   | ||||
| @@ -851,3 +851,15 @@ def iproute2_path(): | ||||
|         logger.warning("Could not execute /sbin/ip or /usr/sbin/ip") | ||||
|         return False | ||||
|     return ipcmd | ||||
|  | ||||
|  | ||||
| def ethtool_path(): | ||||
|     """Find 'ethtool' executable.""" | ||||
|     if os.access('/sbin/ethtool', os.X_OK): | ||||
|         ethtoolcmd = '/sbin/ethtool' | ||||
|     elif os.access('/usr/sbin/ethtool', os.X_OK): | ||||
|         ethtoolcmd = '/usr/sbin/ethtool' | ||||
|     else: | ||||
|         logger.warning("Could not execute /sbin/ethtool or /usr/sbin/ethtool") | ||||
|         return False | ||||
|     return ethtoolcmd | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Zuul
					Zuul