- 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
- if deconfigure:
- # Kill dhclient.
- pidfile_name = '/var/run/dhclient-%s.pid' % interface
- try:
- os.kill(int(read_first_line_of_file(pidfile_name)), signal.SIGTERM)
- except:
- pass
-
- # Remove dhclient pidfile.
- try:
- os.remove(pidfile_name)
- except:
- pass
-
- 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'])
-
-def find_distinguished_pifs(pif):
- """Returns the PIFs on host that own DNS and the default route.
-The peerdns pif will be the one with pif::other-config:peerdns=true, or the mgmt pif if none have this set.
-The gateway pif will be the one with pif::other-config:defaultroute=true, or the mgmt pif if none have this set.
-
-Note: we prune out the bond master pif (if it exists).
-This is because when we are called to bring up an interface with a bond master, it is implicit that
-we should bring down that master."""
-
- pifrec = db.get_pif_record(pif)
-
- pifs = [ __pif for __pif in db.get_all_pifs() if
- (not __pif in get_bond_masters_of_pif(pif)) ]
-
- peerdns_pif = None
- defaultroute_pif = None
-
- # loop through all the pifs on this host looking for one with
- # other-config:peerdns = true, and one with
- # other-config:default-route=true
- for __pif in pifs:
- __pifrec = db.get_pif_record(__pif)
- __oc = __pifrec['other_config']
- if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
- if peerdns_pif == None:
- peerdns_pif = __pif
- else:
- log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \
- (db.get_pif_record(peerdns_pif)['device'], __pifrec['device']))
- if __oc.has_key('defaultroute') and __oc['defaultroute'] == 'true':
- if defaultroute_pif == None:
- defaultroute_pif = __pif
- else:
- log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \
- (db.get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
-
- # If no pif is explicitly specified then use the mgmt pif for peerdns/defaultroute
- if peerdns_pif == None:
- peerdns_pif = management_pif
- if defaultroute_pif == None:
- defaultroute_pif = management_pif
-
- return peerdns_pif, defaultroute_pif
-
-def run_ethtool(device, oc):
- # Run "ethtool -s" if there are any settings.
- settings = []
- if oc.has_key('ethtool-speed'):
- val = oc['ethtool-speed']
- if val in ["10", "100", "1000"]:
- settings += ['speed', val]
- else:
- log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
- if oc.has_key('ethtool-duplex'):
- val = oc['ethtool-duplex']
- if val in ["10", "100", "1000"]:
- settings += ['duplex', 'val']
- else:
- log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
- if oc.has_key('ethtool-autoneg'):
- val = oc['ethtool-autoneg']
- if val in ["true", "on"]:
- settings += ['autoneg', 'on']
- elif val in ["false", "off"]:
- 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)
-
- # 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):
- val = oc["ethtool-" + opt]
- if val in ["true", "on"]:
- offload += [opt, 'on']
- elif val in ["false", "off"]:
- 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)
-
-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_local_port(pif):
- pifrec = db.get_pif_record(pif)
- datapath = datapath_name(pif)
- ipdev = ipdev_name(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":
- pass
- elif pifrec['ip_configuration_mode'] == "Static":
- ifconfig_argv += [pifrec['IP']]
- ifconfig_argv += ['netmask', pifrec['netmask']]
- gateway = pifrec['gateway']
- elif pifrec['ip_configuration_mode'] == "None":
- # Nothing to do.
- pass
- else:
- raise Error("Unknown IP-configuration-mode %s" % pifrec['ip_configuration_mode'])
- 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 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 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' % (network, masklen), 'via', gateway,
- 'dev', ipdev])
-
- # Ethtool.
- run_ethtool(ipdev, nw_oc)
-
- # DHCP.
- if pifrec['ip_configuration_mode'] == "DHCP":
- print
- print "Determining IP information for %s..." % ipdev,
- argv = ['/sbin/dhclient', '-q',
- '-lf', '/var/lib/dhclient/dhclient-%s.leases' % ipdev,
- '-pf', '/var/run/dhclient-%s.pid' % ipdev,
- ipdev]
- if run_command(argv):
- print 'done.'
- 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(['/usr/bin/ovs-cfg-mod', '-vANY:console:emer',
- '-F', '/etc/ovs-vswitchd.conf']
- + commands + ['-c'])
- run_command(['/sbin/service', 'vswitch', 'reload'])
-
-def is_bond_pif(pif):
- pifrec = db.get_pif_record(pif)
- return len(pifrec['bond_master_of']) != 0
-
-def configure_bond(pif):
- pifrec = db.get_pif_record(pif)
- interface = interface_name(pif)
- ipdev = ipdev_name(pif)
- datapath = datapath_name(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 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 = {
- "mode": "balance-slb",
- "miimon": "100",
- "downdelay": "200",
- "updelay": "31000",
- "use_carrier": "1",
- }
- # override defaults with values from other-config whose keys
- # being with "bond-"
- oc = pifrec['other_config']
- overrides = filter(lambda (key,val):
- key.startswith("bond-"), oc.items())
- overrides = map(lambda (key,val): (key[5:], val), overrides)
- bond_options.update(overrides)
- for (name,val) in bond_options.items():
- argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
- return argv
-
-def action_up(pif):
- pifrec = db.get_pif_record(pif)
-
- bridge = bridge_name(pif)
- interface = interface_name(pif)
- ipdev = ipdev_name(pif)
- datapath = datapath_name(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)
- if vlan_slave and is_bond_pif(vlan_slave):
- bond_master = vlan_slave
- elif is_bond_pif(pif):
- bond_master = pif
- else:
- bond_master = None
- if bond_master:
- bond_slaves = get_bond_slaves_of_pif(bond_master)
- else:
- bond_slaves = []
- bond_masters = get_bond_masters_of_pif(pif)
-
- # Support "rpm -e vswitch" gracefully by keeping Centos configuration
- # files up-to-date, even though we don't use them or need them.
- f = configure_pif(pif)
- mode = pifrec['ip_configuration_mode']
- if bridge:
- log("Configuring %s using %s configuration" % (bridge, mode))
- br = open_network_ifcfg(pif)
- configure_network(pif, br)
- br.close()
- f.attach_child(br)
- else:
- log("Configuring %s using %s configuration" % (interface, mode))
- configure_network(pif, f)
- f.close()
- for master in bond_masters:
- master_bridge = bridge_name(master)
- removed = unconfigure_pif(master)
- f.attach_child(removed)
- if master_bridge:
- removed = open_network_ifcfg(master)
- log("Unlinking stale file %s" % removed.path())
- removed.unlink()
- f.attach_child(removed)
-
- # /etc/xensource/scripts/vif needs to know where to add VIFs.
- if vlan_slave:
- 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()
- f.attach_child(br)
-
- # Update all configuration files (both ours and Centos's).
- 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_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).
- #
- # Conversely, if we are bringing up an interface that has bond
- # masters, remove IP addresses from the bond master (because we
- # are implicitly being asked to take it down).
- for bond_pif in bond_slaves + bond_masters:
- 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] + physdev_names + bond_masters
- if vlan_slave and bond_master:
- del_ports += [interface_name(bond_master)]
-
- # What ports do we need to add to the datapath?
- #
- # We definitely need the ipdev, and ordinarily we want the
- # physical devices too, but for bonds we need the bond as bridge
- # port.
- add_ports = [ipdev, datapath]
- if not bond_master:
- add_ports += physdev_names
- else:
- add_ports += [interface_name(bond_master)]
-
- # What ports do we need to delete?
- #
- # - All the ports that we add, to avoid duplication and to drop
- # them from another datapath in case they're misassigned.
- #
- # - The physical devices, since they will either be in add_ports
- # or added to the bonding device (see below).
- #
- # - 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 + physdev_names + bond_masters
-
- # What networks does this datapath carry?
- #
- # - The network corresponding to the datapath's 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']}):
- net = db.get_pif_record(nwpif)['network']
- network_uuids += [db.get_network_record(net)['uuid']]
-
- # Bring up bond slaves early, because ovs-vswitchd initially
- # 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_physdev_pifs = []
- for slave in bond_slaves:
- 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 = []
- for port in set(del_ports):
- argv += interface_deconfigure_commands(port)
- for port in set(add_ports):
- argv += ['--add=bridge.%s.port=%s' % (datapath, port)]
- if vlan_slave:
- argv += ['--add=vlan.%s.tag=%s' % (ipdev, pifrec['VLAN'])]
- argv += ['--add=iface.%s.internal=true' % (ipdev)]
-
- # xapi creates a bridge by the name of the ipdev and requires
- # that the IP address will be on it. We need to delete this
- # bridge because we need that device to be a member of our
- # datapath.
- argv += ['--del-match=bridge.%s.[!0-9]*' % ipdev]
-
- # xapi insists that its attempts to create the bridge succeed,
- # so force that to happen.
- argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)]
- else:
- try:
- os.unlink("%s/br-%s" % (vswitch_state_dir, bridge))
- except OSError:
- pass
- argv += ['--del-match=bridge.%s.xs-network-uuids=*' % datapath]
- argv += ['--add=bridge.%s.xs-network-uuids=%s' % (datapath, uuid)
- for uuid in set(network_uuids)]
- if bond_master:
- argv += configure_bond(bond_master)
- modify_config(argv)
-
- # 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_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")
-
- if bond_slaves:
- # There seems to be a race somewhere: without this sleep, using
- # XenCenter to create a bond that becomes the management interface
- # fails with "The underlying connection was closed: A connection that
- # was expected to be kept alive was closed by the server." on every
- # second or third try, even though /var/log/messages doesn't show
- # anything unusual.
- #
- # The race is probably present even without vswitch, but bringing up a
- # bond without vswitch involves a built-in pause of 10 seconds or more
- # to wait for the bond to transition from learning to forwarding state.
- time.sleep(5)
-
-def action_down(pif):
- rec = db.get_pif_record(pif)
- interface = interface_name(pif)
- bridge = bridge_name(pif)
- ipdev = ipdev_name(pif)
-
- # Support "rpm -e vswitch" gracefully by keeping Centos configuration
- # files up-to-date, even though we don't use them or need them.
- f = unconfigure_pif(pif)
- if bridge:
- br = open_network_ifcfg(pif)
- log("Unlinking stale file %s" % br.path())
- br.unlink()
- f.attach_child(br)
- try:
- f.apply()
- f.commit()
- except Error, e:
- log("action_down failed to apply changes: %s" % e.msg)
- f.revert()
- raise
-
- argv = []
- if rec['VLAN'] != '-1':
- # Get rid of the VLAN device itself.
- down_netdev(ipdev)
- argv += interface_deconfigure_commands(ipdev)
-
- # If the VLAN's slave is attached, stop here.
- slave = get_vlan_slave_of_pif(pif)
- if db.get_pif_record(slave)['currently_attached']:
- log("VLAN slave is currently attached")
- modify_config(argv)
- return
-
- # If the VLAN's slave has other VLANs that are attached, stop here.
- masters = get_vlan_masters_of_pif(slave)
- for m in masters:
- if m != pif and db.get_pif_record(m)['currently_attached']:
- log("VLAN slave has other master %s" % interface_naem(m))
- modify_config(argv)
- return
-
- # Otherwise, take down the VLAN's slave too.
- log("No more masters, bring down vlan slave %s" % interface_name(slave))
- pif = slave
- else:
- # Stop here if this PIF has attached VLAN masters.
- vlan_masters = get_vlan_masters_of_pif(pif)
- log("VLAN masters of %s - %s" % (rec['device'], [interface_name(m) for m in vlan_masters]))
- for m in vlan_masters:
- if db.get_pif_record(m)['currently_attached']:
- log("Leaving %s up due to currently attached VLAN master %s" % (interface, interface_name(m)))
- return
-
- # pif is now either a bond or a physical device which needs to be
- # brought down. pif might have changed so re-check all its attributes.
- rec = db.get_pif_record(pif)
- interface = interface_name(pif)
- bridge = bridge_name(pif)
- ipdev = ipdev_name(pif)
-
-
- bond_slaves = get_bond_slaves_of_pif(pif)
- log("bond slaves of %s - %s" % (rec['device'], [interface_name(s) for s in bond_slaves]))
- for slave in bond_slaves:
- slave_interface = interface_name(slave)
- log("bring down bond slave %s" % slave_interface)
- argv += interface_deconfigure_commands(slave_interface)
- down_netdev(slave_interface)
-
- argv += interface_deconfigure_commands(ipdev)
- down_netdev(ipdev)
-
- argv += ['--del-match', 'bridge.%s.*' % datapath_name(pif)]
- argv += ['--del-match', 'bonding.%s.[!0-9]*' % interface]
- modify_config(argv)
-
-def action_rewrite(pif):
- # Support "rpm -e vswitch" gracefully by keeping Centos configuration
- # files up-to-date, even though we don't use them or need them.
- pifrec = db.get_pif_record(pif)
- f = configure_pif(pif)
- interface = interface_name(pif)
- bridge = bridge_name(pif)
- mode = pifrec['ip_configuration_mode']
- if bridge:
- log("Configuring %s using %s configuration" % (bridge, mode))
- br = open_network_ifcfg(pif)
- configure_network(pif, br)
- br.close()
- f.attach_child(br)
- else:
- log("Configuring %s using %s configuration" % (interface, mode))
- configure_network(pif, f)
- f.close()
- try:
- f.apply()
- f.commit()
- except Error, e:
- log("failed to apply changes: %s" % e.msg)
- f.revert()
- raise
-
- # We have no code of our own to run here.
- pass
-
-def main(argv=None):
- global output_directory, management_pif
-
- session = None
- pif_uuid = None
- pif = None
-
- force_interface = None
- force_management = False
-
- if argv is None:
- argv = sys.argv
-
- try:
- try:
- shortops = "h"
- longops = [ "output-directory=",
- "pif=", "pif-uuid=",
- "session=",
- "force=",
- "force-interface=",
- "management",
- "test-mode",
- "device=", "mode=", "ip=", "netmask=", "gateway=",
- "help" ]
- arglist, args = getopt.gnu_getopt(argv[1:], shortops, longops)
- except getopt.GetoptError, msg:
- raise Usage(msg)
-
- force_rewrite_config = {}
-
- for o,a in arglist:
- if o == "--output-directory":
- output_directory = a
- elif o == "--pif":
- pif = a
- elif o == "--pif-uuid":
- pif_uuid = a
- elif o == "--session":
- session = a
- elif o == "--force-interface" or o == "--force":
- force_interface = a
- elif o == "--management":
- force_management = True
- elif o in ["--device", "--mode", "--ip", "--netmask", "--gateway"]:
- force_rewrite_config[o[2:]] = a
- elif o == "-h" or o == "--help":
- print __doc__ % {'command-name': os.path.basename(argv[0])}
- return 0
-
- if not debug_mode():
- syslog.openlog(os.path.basename(argv[0]))
- log("Called as " + str.join(" ", argv))
- if len(args) < 1:
- raise Usage("Required option <action> not present")
- if len(args) > 1:
- raise Usage("Too many arguments")
-
- action = args[0]
- # backwards compatibility
- if action == "rewrite-configuration": action = "rewrite"
-
- if output_directory and ( session or pif ):
- raise Usage("--session/--pif cannot be used with --output-directory")
- if ( session or pif ) and pif_uuid:
- raise Usage("--session/--pif and --pif-uuid are mutually exclusive.")
- if ( session and not pif ) or ( not session and pif ):
- raise Usage("--session and --pif must be used together.")
- if force_interface and ( session or pif or pif_uuid ):
- raise Usage("--force is mutually exclusive with --session, --pif and --pif-uuid")
- if len(force_rewrite_config) and not (force_interface and action == "rewrite"):
- raise Usage("\"--force rewrite\" needed for --device, --mode, --ip, --netmask, and --gateway")
-
- if action == "init-dbcache" and arglist:
- raise Usage("\"init-dbcache\" action does not accept any options")
-
- global db
- if force_interface:
- log("Force interface %s %s" % (force_interface, action))
-
- if action == "rewrite":
- action_force_rewrite(force_interface, force_rewrite_config)
- else:
- db = DatabaseCache(cache_file=dbcache_file)
- pif = db.get_pif_by_bridge(force_interface)
- management_pif = db.get_management_pif()