X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=xenserver%2Fopt_xensource_libexec_interface-reconfigure;h=27195ad93e69c60f3f59316edcf0de53cd77711f;hb=057fed2b5cfe81bf3ab328a9514a1caf0dfa4f55;hp=5f1ca8258e5759b01f0581a5e00f35d57363f16e;hpb=6dd3fad481b5d801695c2b0529c7d37cac2c9b19;p=openvswitch diff --git a/xenserver/opt_xensource_libexec_interface-reconfigure b/xenserver/opt_xensource_libexec_interface-reconfigure index 5f1ca825..27195ad9 100755 --- a/xenserver/opt_xensource_libexec_interface-reconfigure +++ b/xenserver/opt_xensource_libexec_interface-reconfigure @@ -1,26 +1,30 @@ #!/usr/bin/python # -# Copyright (c) Citrix Systems 2008. All rights reserved. +# Copyright (c) 2008,2009 Citrix Systems, Inc. All rights reserved. # Copyright (c) 2009 Nicira Networks. # """Usage: - %(command-name)s --session --pif [up|down|rewrite] - %(command-name)s --force [up|down|rewrite ] + %(command-name)s up + %(command-name)s down + %(command-name)s [] rewrite + %(command-name)s --force up + %(command-name)s --force down + %(command-name)s --force rewrite --device= %(command-name)s --force all down - where, - = --device= --mode=dhcp - = --device= --mode=static --ip= --netmask= [--gateway=] + where is one of: + --session --pif + --pif-uuid + and is one of: + --mode=dhcp + --mode=static --ip= --netmask= [--gateway=] Options: --session A session reference to use to access the xapi DB - --pif A PIF reference. - --force-interface An interface name. Mutually exclusive with --session/--pif. - - Either both --session and --pif or just --pif-uuid. - - is either "up" or "down" or "rewrite" + --pif A PIF reference within the session. + --pif-uuid The UUID of a PIF. + --force An interface name. """ # @@ -28,7 +32,6 @@ # # --output-directory= Write configuration to . Also disables actually # raising/lowering the interfaces -# --pif-uuid A PIF UUID, use instead of --session/--pif. # # # @@ -38,30 +41,13 @@ # 3. A network may have an associated bridge, allowing vifs to be attached # 4. A network may be bridgeless (there's no point having a bridge over a storage pif) -# XXX: --force-interface=all down - -# XXX: --force-interface rewrite - -# XXX: Sometimes this leaves "orphaned" datapaths, e.g. a datapath whose -# only port is the local port. Should delete those. - -# XXX: This can leave crud in ovs-vswitchd.conf in this scenario: -# - Create bond in XenCenter. -# - Create VLAN on bond in XenCenter. -# - Attempt to delete bond in XenCenter (this will fail because there -# is a VLAN on the bond, although the error may not be reported -# until the next step) -# - Delete VLAN in XenCenter. -# - Delete bond in XenCenter. -# At this point there will still be some configuration data for the bond -# or the VLAN in ovs-vswitchd.conf. - import XenAPI import os, sys, getopt, time, signal import syslog import traceback import time import re +import random from xml.dom.minidom import getDOMImplementation from xml.dom.minidom import parse as parseXML @@ -70,8 +56,8 @@ output_directory = None db = None management_pif = None -dbcache_file = "/etc/ovs-vswitch.dbcache" -vswitch_config_dir = "/etc/openvswitch" +vswitch_state_dir = "/var/lib/openvswitch/" +dbcache_file = vswitch_state_dir + "dbcache" class Usage(Exception): def __init__(self, msg): @@ -249,6 +235,29 @@ 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): + for device in os.listdir("/sys/class/net"): + dev_mac = get_netdev_mac(device) + if (dev_mac and mac.lower() == dev_mac.lower() and + get_netdev_tx_queue_len(device)): + return device + return None + # # Helper functions for encoding/decoding database attributes to/from XML. # @@ -346,6 +355,12 @@ PIF_ATTRS = { 'uuid': (str_to_xml,str_from_xml), 'MAC': (str_to_xml,str_from_xml), 'other_config': (lambda x, p, t, v: otherconfig_to_xml(x, p, v, PIF_OTHERCONFIG_ATTRS), lambda n: otherconfig_from_xml(n, PIF_OTHERCONFIG_ATTRS)), + + # Special case: We write the current value + # PIF.currently-attached to the cache but since it will + # not be valid when we come to use the cache later + # (i.e. after a reboot) we always read it as False. + 'currently_attached': (bool_to_xml, lambda n: False), } PIF_OTHERCONFIG_ATTRS = [ 'domain', 'peerdns', 'defaultroute', 'mtu', 'static-routes' ] + \ @@ -426,7 +441,11 @@ class DatabaseCache(object): rec = session.xenapi.network.get_record(n) self.__networks[n] = {} for f in NETWORK_ATTRS: - self.__networks[n][f] = rec[f] + if f == "PIFs": + # drop PIFs on other hosts + self.__networks[n][f] = [p for p in rec[f] if self.__pif_on_host(p)] + else: + self.__networks[n][f] = rec[f] self.__networks[n]['other_config'] = {} for f in NETWORK_OTHERCONFIG_ATTRS: if not rec['other_config'].has_key(f): continue @@ -653,23 +672,29 @@ 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 get_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_physdev_pifs(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 get_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 get_physdev_pifs(pif)] def log_pif_action(action, pif): pifrec = db.get_pif_record(pif) @@ -678,10 +703,7 @@ def log_pif_action(action, pif): rec['ip_configuration_mode'] = pifrec['ip_configuration_mode'] rec['action'] = action rec['interface-name'] = interface_name(pif) - if action == "rewrite": - rec['message'] = "Rewrite PIF %(uuid)s configuration" % rec - else: - rec['message'] = "Bring %(action)s PIF %(uuid)s" % rec + rec['message'] = "Bring %(action)s PIF %(uuid)s" % rec log("%(message)s: %(interface-name)s configured as %(ip_configuration_mode)s" % rec) def get_bond_masters_of_pif(pif): @@ -764,31 +786,76 @@ 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: + file = open(name, 'r') + return file.readline().rstrip('\n') + finally: + if file != None: + file.close() + def down_netdev(interface, deconfigure=True): if not interface_exists(interface): log("down_netdev: interface %s does not exist, ignoring" % interface) return - argv = ["/sbin/ifconfig", interface, 'down'] if deconfigure: - argv += ['0.0.0.0'] - # Kill dhclient. pidfile_name = '/var/run/dhclient-%s.pid' % interface - pidfile = None try: - pidfile = open(pidfile_name, 'r') - os.kill(int(pidfile.readline()), signal.SIGTERM) + os.kill(int(read_first_line_of_file(pidfile_name)), signal.SIGTERM) except: pass - if pidfile != None: - pidfile.close() # Remove dhclient pidfile. try: os.remove(pidfile_name) except: pass - run_command(argv) + + run_command(["/sbin/ifconfig", interface, '0.0.0.0']) + + run_command(["/sbin/ifconfig", interface, 'down']) def up_netdev(interface): run_command(["/sbin/ifconfig", interface, 'up']) @@ -837,8 +904,8 @@ we should bring down that master.""" return peerdns_pif, defaultroute_pif -def ethtool_settings(oc): - # Options for "ethtool -s" +def run_ethtool(device, oc): + # Run "ethtool -s" if there are any settings. settings = [] if oc.has_key('ethtool-speed'): val = oc['ethtool-speed'] @@ -860,8 +927,10 @@ def ethtool_settings(oc): settings += ['autoneg', 'off'] else: log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val) + if settings: + run_command(['/sbin/ethtool', '-s', device] + settings) - # Options for "ethtool -K" + # Run "ethtool -K" if there are any offload settings. offload = [] for opt in ("rx", "tx", "sg", "tso", "ufo", "gso"): if oc.has_key("ethtool-" + opt): @@ -872,10 +941,19 @@ def ethtool_settings(oc): offload += [opt, 'off'] else: log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val)) + if offload: + run_command(['/sbin/ethtool', '-K', device] + offload) - return settings, offload +def mtu_setting(oc): + if oc.has_key('mtu'): + try: + int(oc['mtu']) # Check that the value is an integer + return ['mtu', oc['mtu']] + except ValueError, x: + log("Invalid value for mtu = %s" % mtu) + return [] -def configure_netdev(pif): +def configure_local_port(pif): pifrec = db.get_pif_record(pif) datapath = datapath_name(pif) ipdev = ipdev_name(pif) @@ -883,6 +961,10 @@ def configure_netdev(pif): nw = pifrec['network'] nwrec = db.get_network_record(nw) + pif_oc = pifrec['other_config'] + nw_oc = nwrec['other_config'] + + # IP (except DHCP) and MTU. ifconfig_argv = ['/sbin/ifconfig', ipdev, 'up'] gateway = '' if pifrec['ip_configuration_mode'] == "DHCP": @@ -896,45 +978,37 @@ def configure_netdev(pif): pass else: raise Error("Unknown IP-configuration-mode %s" % pifrec['ip_configuration_mode']) - - oc = {} - if pifrec.has_key('other_config'): - oc = pifrec['other_config'] - if oc.has_key('mtu'): - int(oc['mtu']) # Check that the value is an integer - ifconfig_argv += ['mtu', oc['mtu']] - + ifconfig_argv += mtu_setting(nw_oc) run_command(ifconfig_argv) (peerdns_pif, defaultroute_pif) = find_distinguished_pifs(pif) + # /etc/resolv.conf if peerdns_pif == pif: f = ConfigurationFile('resolv.conf', "/etc") - if oc.has_key('domain'): - f.write("search %s\n" % oc['domain']) + if pif_oc.has_key('domain'): + f.write("search %s\n" % pif_oc['domain']) for dns in pifrec['DNS'].split(","): f.write("nameserver %s\n" % dns) f.close() f.apply() f.commit() + # Routing. if defaultroute_pif == pif and gateway != '': run_command(['/sbin/ip', 'route', 'replace', 'default', 'via', gateway, 'dev', ipdev]) - - if oc.has_key('static-routes'): - for line in oc['static-routes'].split(','): + if nw_oc.has_key('static-routes'): + for line in nw_oc['static-routes'].split(','): network, masklen, gateway = line.split('/') run_command(['/sbin/ip', 'route', 'add', - '%s/%s' % (netmask, masklen), 'via', gateway, + '%s/%s' % (network, masklen), 'via', gateway, 'dev', ipdev]) - settings, offload = ethtool_settings(oc) - if settings: - run_command(['/sbin/ethtool', '-s', ipdev] + settings) - if offload: - run_command(['/sbin/ethtool', '-K', ipdev] + offload) + # Ethtool. + run_ethtool(ipdev, nw_oc) + # DHCP. if pifrec['ip_configuration_mode'] == "DHCP": print print "Determining IP information for %s..." % ipdev, @@ -947,8 +1021,16 @@ def configure_netdev(pif): else: print 'failed.' +def configure_physdev(pif): + pifrec = db.get_pif_record(pif) + device = pifrec['device'] + oc = pifrec['other_config'] + + run_command(['/sbin/ifconfig', device, 'up'] + mtu_setting(oc)) + run_ethtool(device, oc) + def modify_config(commands): - run_command(['/root/vswitch/bin/ovs-cfg-mod', '-vANY:console:emer', + run_command(['/usr/bin/ovs-cfg-mod', '-vANY:console:emer', '-F', '/etc/ovs-vswitchd.conf'] + commands + ['-c']) run_command(['/sbin/service', 'vswitch', 'reload']) @@ -962,11 +1044,15 @@ def configure_bond(pif): interface = interface_name(pif) ipdev = ipdev_name(pif) datapath = datapath_name(pif) - physdevs = physdev_names(pif) + physdev_names = get_physdev_names(pif) argv = ['--del-match=bonding.%s.[!0-9]*' % interface] argv += ["--add=bonding.%s.slave=%s" % (interface, slave) - for slave in physdevs] + for slave in physdev_names] + argv += ['--add=bonding.%s.fake-iface=true' % interface] + + if pifrec['MAC'] != "": + argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])] # Bonding options. bond_options = { @@ -994,7 +1080,8 @@ def action_up(pif): interface = interface_name(pif) ipdev = ipdev_name(pif) datapath = datapath_name(pif) - physdevs = physdev_names(pif) + physdev_pifs = get_physdev_pifs(pif) + physdev_names = get_physdev_names(pif) vlan_slave = None if pifrec['VLAN'] != '-1': vlan_slave = get_vlan_slave_of_pif(pif) @@ -1036,9 +1123,9 @@ def action_up(pif): # /etc/xensource/scripts/vif needs to know where to add VIFs. if vlan_slave: - if not os.path.exists(vswitch_config_dir): - os.mkdir(vswitch_config_dir) - br = ConfigurationFile("br-%s" % bridge, vswitch_config_dir) + if not os.path.exists(vswitch_state_dir): + os.mkdir(vswitch_state_dir) + br = ConfigurationFile("br-%s" % bridge, vswitch_state_dir) br.write("VLAN_SLAVE=%s\n" % datapath) br.write("VLAN_VID=%s\n" % pifrec['VLAN']) br.close() @@ -1048,10 +1135,15 @@ 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: + remap_pif(physdev_pif) + # "ifconfig down" the network device and delete its IP address, etc. down_netdev(ipdev) - for physdev in physdevs: - down_netdev(physdev) + for physdev_name in physdev_names: + down_netdev(physdev_name) # If we are bringing up a bond, remove IP addresses from the # slaves (because we are implicitly being asked to take them down). @@ -1063,7 +1155,7 @@ def action_up(pif): run_command(["/sbin/ifconfig", ipdev_name(bond_pif), '0.0.0.0']) # Remove all keys related to pif and any bond masters linked to PIF. - del_ports = [ipdev] + physdevs + bond_masters + del_ports = [ipdev] + physdev_names + bond_masters if vlan_slave and bond_master: del_ports += [interface_name(bond_master)] @@ -1074,7 +1166,7 @@ def action_up(pif): # port. add_ports = [ipdev, datapath] if not bond_master: - add_ports += physdevs + add_ports += physdev_names else: add_ports += [interface_name(bond_master)] @@ -1089,7 +1181,7 @@ def action_up(pif): # - The bond masters for pif. (Ordinarily pif shouldn't have any # bond masters. If it does then interface-reconfigure is # implicitly being asked to take them down.) - del_ports = add_ports + physdevs + bond_masters + del_ports = add_ports + physdev_names + bond_masters # What networks does this datapath carry? # @@ -1098,7 +1190,7 @@ def action_up(pif): # - The networks corresponding to any VLANs attached to the # datapath's PIF. network_uuids = [] - for nwpif in db.get_pifs_by_device({'device': pifrec['device']}): + for nwpif in db.get_pifs_by_device(pifrec['device']): net = db.get_pif_record(nwpif)['network'] network_uuids += [db.get_network_record(net)['uuid']] @@ -1106,11 +1198,11 @@ def action_up(pif): # enables or disables bond slaves based on whether carrier is # detected when they are added, and a network device that is down # always reports "no carrier". - bond_slave_physdevs = [] + bond_slave_physdev_pifs = [] for slave in bond_slaves: - bond_slave_physdevs += physdev_names(slave) - for slave_physdev in bond_slave_physdevs: - up_netdev(slave_physdev) + bond_slave_physdev_pifs += get_physdev_pifs(slave) + for slave_physdev_pif in set(bond_slave_physdev_pifs): + configure_physdev(slave_physdev_pif) # Now modify the ovs-vswitchd config file. argv = [] @@ -1133,7 +1225,7 @@ def action_up(pif): argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)] else: try: - os.unlink("%s/br-%s" % (vswitch_config_dir, bridge)) + os.unlink("%s/br-%s" % (vswitch_state_dir, bridge)) except OSError: pass argv += ['--del-match=bridge.%s.xs-network-uuids=*' % datapath] @@ -1143,15 +1235,15 @@ def action_up(pif): argv += configure_bond(bond_master) modify_config(argv) - # Configure network devices. - configure_netdev(pif) - # Bring up VLAN slave, plus physical devices other than bond # slaves (which we brought up earlier). if vlan_slave: up_netdev(ipdev_name(vlan_slave)) - for physdev in set(physdevs) - set(bond_slave_physdevs): - up_netdev(physdev) + for physdev_pif in set(physdev_pifs) - set(bond_slave_physdev_pifs): + configure_physdev(physdev_pif) + + # Configure network device for local port. + configure_local_port(pif) # Update /etc/issue (which contains the IP address of the management interface) os.system("/sbin/update-issue") @@ -1371,35 +1463,37 @@ def main(argv=None): if pif_uuid: pif = db.get_pif_by_uuid(pif_uuid) - - if not pif: - raise Usage("No PIF given") - if force_management: - # pif is going to be the management pif - management_pif = pif + if action == "rewrite" and not pif: + pass else: - # pif is not going to be the management pif. - # Search DB cache for pif on same host with management=true - pifrec = db.get_pif_record(pif) - management_pif = db.get_management_pif() + if not pif: + raise Usage("No PIF given") - log_pif_action(action, pif) + if force_management: + # pif is going to be the management pif + management_pif = pif + else: + # pif is not going to be the management pif. + # Search DB cache for pif on same host with management=true + pifrec = db.get_pif_record(pif) + management_pif = db.get_management_pif() - if not check_allowed(pif): - return 0 + log_pif_action(action, pif) - if action == "up": - action_up(pif) - elif action == "down": - action_down(pif) - elif action == "rewrite": - action_rewrite(pif) - else: - raise Usage("Unknown action %s" % action) + if not check_allowed(pif): + return 0 + + if action == "up": + action_up(pif) + elif action == "down": + action_down(pif) + elif action == "rewrite": + action_rewrite(pif) + else: + raise Usage("Unknown action %s" % action) # Save cache. - pifrec = db.get_pif_record(pif) db.save(dbcache_file) except Usage, err: