From 2bb451b698fa45c4c616ba721bd8fc6d07064bb7 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 6 Aug 2009 17:01:53 -0700 Subject: [PATCH] xenserver: Rename network devices to match MAC addresses of physical PIFs. XenServer does not rely on Linux to keep the naming of network devices stable from one boot to the next. Instead, it requires interface-reconfigure to ensure that network devices are named such that they have the MAC address specified for the corresponding physical PIF in the xapi database. At one point, we fulfilled this requirement by calling out to the Centos ifup/ifdown scripts, which rename netdevs as necessary to match the "HWADDR=" lines in /etc/sysconfig/network-scripts/ifcfg-. When we rewrote interface-reconfigure not to use those scripts, however, we accidentally dropped that support. This commit adds back in that renaming. Bug NIC-20. --- ...pt_xensource_libexec_interface-reconfigure | 101 ++++++++++++++++-- 1 file changed, 90 insertions(+), 11 deletions(-) diff --git a/xenserver/opt_xensource_libexec_interface-reconfigure b/xenserver/opt_xensource_libexec_interface-reconfigure index 6de62b387..481bddd2e 100755 --- a/xenserver/opt_xensource_libexec_interface-reconfigure +++ b/xenserver/opt_xensource_libexec_interface-reconfigure @@ -63,6 +63,7 @@ import traceback import time import re import pickle +import random output_directory = None @@ -248,6 +249,33 @@ def check_allowed(pif): def interface_exists(i): return os.path.exists("/sys/class/net/" + i) +def get_netdev_mac(device): + try: + return read_first_line_of_file("/sys/class/net/%s/address" % device) + except: + # Probably no such device. + return None + +def get_netdev_tx_queue_len(device): + try: + return int(read_first_line_of_file("/sys/class/net/%s/tx_queue_len" + % device)) + except: + # Probably no such device. + return None + +def get_netdev_by_mac(mac): + maybe = None + for device in os.listdir("/sys/class/net"): + dev_mac = get_netdev_by_mac(device) + if dev_mac and mac.lower() == dev_mac.lower(): + if get_netdev_tx_queue_len(device): + return device + if not maybe: + # Probably a datapath internal port. + maybe = device + return maybe + class DatabaseCache(object): def __init__(self, session_ref=None, cache_file=None): if session_ref and cache_file: @@ -433,24 +461,30 @@ The ipdev name is the same as the bridge name. pifrec = db.get_pif_record(pif) return bridge_name(pif) -def physdev_names(pif): - """Return the name(s) of the physical network device(s) associated with pif. -For a VLAN PIF, the physical devices are the VLAN slave's physical devices. -For a bond master PIF, the physical devices are the bond slaves. -For a non-VLAN, non-bond master PIF, the physical device is the PIF itself. +def physdev_pifs(pif): + """Return the PIFs for the physical network device(s) associated with pif. +For a VLAN PIF, this is the VLAN slave's physical device PIF. +For a bond master PIF, these are the bond slave PIFs. +For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF. """ pifrec = db.get_pif_record(pif) if pifrec['VLAN'] != '-1': - return physdev_names(get_vlan_slave_of_pif(pif)) + return [get_vlan_slave_of_pif(pif)] elif len(pifrec['bond_master_of']) != 0: - physdevs = [] - for slave in get_bond_slaves_of_pif(pif): - physdevs += physdev_names(slave) - return physdevs + return get_bond_slaves_of_pif(pif) else: - return [pifrec['device']] + return [pif] + +def physdev_names(pif): + """Return the name(s) of the physical network device(s) associated with pif. +For a VLAN PIF, the physical devices are the VLAN slave's physical devices. +For a bond master PIF, the physical devices are the bond slaves. +For a non-VLAN, non-bond master PIF, the physical device is the PIF itself. +""" + + return [db.get_pif_record(phys)['device'] for phys in physdev_pifs(pif)] def log_pif_action(action, pif): pifrec = db.get_pif_record(pif) @@ -543,6 +577,46 @@ def run_command(command): return False return True +def rename_netdev(old_name, new_name): + log("Changing the name of %s to %s" % (old_name, new_name)) + run_command(['/sbin/ifconfig', old_name, 'down']) + if not run_command(['/sbin/ip', 'link', 'set', old_name, + 'name', new_name]): + raise Error("Could not rename %s to %s" % (old_name, new_name)) + +# Check whether 'pif' exists and has the correct MAC. +# If not, try to find a device with the correct MAC and rename it. +# 'already_renamed' is used to avoid infinite recursion. +def remap_pif(pif, already_renamed=[]): + pifrec = db.get_pif_record(pif) + device = pifrec['device'] + mac = pifrec['MAC'] + + # Is there a network device named 'device' at all? + device_exists = interface_exists(device) + if device_exists: + # Yes. Does it have MAC 'mac'? + found_mac = get_netdev_mac(device) + if found_mac and mac.lower() == found_mac.lower(): + # Yes, everything checks out the way we want. Nothing to do. + return + else: + log("No network device %s" % device) + + # What device has MAC 'mac'? + cur_device = get_netdev_by_mac(mac) + if not cur_device: + log("No network device has MAC %s" % mac) + return + + # First rename 'device', if it exists, to get it out of the way + # for 'cur_device' to replace it. + if device_exists: + rename_netdev(device, "dev%d" % random.getrandbits(24)) + + # Rename 'cur_device' to 'device'. + rename_netdev(cur_device, device) + def read_first_line_of_file(name): file = None try: @@ -838,6 +912,11 @@ def action_up(pif): f.apply() f.commit() + # Check the MAC address of each network device and remap if + # necessary to make names match our expectations. + for physdev_pif in physdev_pifs(pif): + remap_pif(physdev_pif) + # "ifconfig down" the network device and delete its IP address, etc. down_netdev(ipdev) for physdev in physdevs: -- 2.43.0