"NOT-APPLIED":"NOT-APPLIED", "APPLIED":"APPLIED",
"REVERTED":"REVERTED", "COMMITTED": "COMMITTED"}
- def __init__(self, fname, path):
+ def __init__(self, fname, path="/etc/sysconfig/network-scripts"):
self.__state = self.__STATE['OPEN']
self.__fname = fname
bond_master = pif
else:
bond_master = None
+ 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_config_dir):
+ os.mkdir(vswitch_config_dir)
+ br = ConfigurationFile("br-%s" % bridge, vswitch_config_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()
# "ifconfig down" the network device and delete its IP address, etc.
down_netdev(ipdev)
down_netdev(physdev)
# Remove all keys related to pif and any bond masters linked to PIF.
- del_ports = [ipdev] + physdevs + get_bond_masters_of_pif(pif)
+ del_ports = [ipdev] + physdevs + bond_masters
if vlan_slave and bond_master:
del_ports += [interface_name(bond_master)]
# - 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 + get_bond_masters_of_pif(pif)
+ del_ports = add_ports + physdevs + bond_masters
# Now modify the ovs-vswitchd config file.
argv = []
# xapi insists that its attempts to create the bridge succeed,
# so force that to happen.
argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)]
-
- # /etc/xensource/scripts/vif needs to know where to add VIFs.
- if not os.path.exists(vswitch_config_dir):
- os.mkdir(vswitch_config_dir)
- f = ConfigurationFile("br-%s" % bridge, vswitch_config_dir)
- f.write("VLAN_SLAVE=%s\n" % datapath)
- f.write("VLAN_VID=%s\n" % pifrec['VLAN'])
- f.close()
- f.apply()
- f.commit()
else:
try:
os.unlink("%s/br-%s" % (vswitch_config_dir, bridge))
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.
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)
- db.save(dbcache_file, {'host': pifrec['host']})
+ 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
action_rewrite(pif)
else:
raise Usage("Unknown action %s" % action)
+
+ # Save cache.
+ pifrec = db.get_pif_record(pif)
+ db.save(dbcache_file, {'host': pifrec['host']})
except Usage, err:
print >>sys.stderr, err.msg
return 1
return 0
+\f
+# The following code allows interface-reconfigure to keep Centos
+# network configuration files up-to-date, even though the vswitch
+# never uses them. In turn, that means that "rpm -e vswitch" does not
+# have to update any configuration files.
+
+def configure_ethtool(oc, f):
+ # Options for "ethtool -s"
+ settings = None
+ setting_opts = ["autoneg", "speed", "duplex"]
+ # Options for "ethtool -K"
+ offload = None
+ offload_opts = ["rx", "tx", "sg", "tso", "ufo", "gso"]
+
+ for opt in [opt for opt in setting_opts + offload_opts if oc.has_key("ethtool-" + opt)]:
+ val = oc["ethtool-" + opt]
+
+ if opt in ["speed"]:
+ if val in ["10", "100", "1000"]:
+ val = "speed " + val
+ else:
+ log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
+ val = None
+ elif opt in ["duplex"]:
+ if val in ["half", "full"]:
+ val = "duplex " + val
+ else:
+ log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
+ val = None
+ elif opt in ["autoneg"] + offload_opts:
+ if val in ["true", "on"]:
+ val = opt + " on"
+ elif val in ["false", "off"]:
+ val = opt + " off"
+ else:
+ log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
+ val = None
+
+ if opt in setting_opts:
+ if val and settings:
+ settings = settings + " " + val
+ else:
+ settings = val
+ elif opt in offload_opts:
+ if val and offload:
+ offload = offload + " " + val
+ else:
+ offload = val
+
+ if settings:
+ f.write("ETHTOOL_OPTS=\"%s\"\n" % settings)
+ if offload:
+ f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % offload)
+
+def configure_mtu(oc, f):
+ if not oc.has_key('mtu'):
+ return
+
+ try:
+ mtu = int(oc['mtu'])
+ f.write("MTU=%d\n" % mtu)
+ except ValueError, x:
+ log("Invalid value for mtu = %s" % mtu)
+
+def configure_static_routes(interface, oc, f):
+ """Open a route-<interface> file for static routes.
+
+ Opens the static routes configuration file for interface and writes one
+ line for each route specified in the network's other config "static-routes" value.
+ E.g. if
+ interface ( RO): xenbr1
+ other-config (MRW): static-routes: 172.16.0.0/15/192.168.0.3,172.18.0.0/16/192.168.0.4;...
+
+ Then route-xenbr1 should be
+ 172.16.0.0/15 via 192.168.0.3 dev xenbr1
+ 172.18.0.0/16 via 192.168.0.4 dev xenbr1
+ """
+ fname = "route-%s" % interface
+ if oc.has_key('static-routes'):
+ # The key is present - extract comma seperates entries
+ lines = oc['static-routes'].split(',')
+ else:
+ # The key is not present, i.e. there are no static routes
+ lines = []
+
+ child = ConfigurationFile(fname)
+ child.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
+ (os.path.basename(child.path()), os.path.basename(sys.argv[0])))
+
+ try:
+ for l in lines:
+ network, masklen, gateway = l.split('/')
+ child.write("%s/%s via %s dev %s\n" % (network, masklen, gateway, interface))
+
+ f.attach_child(child)
+ child.close()
+
+ except ValueError, e:
+ log("Error in other-config['static-routes'] format for network %s: %s" % (interface, e))
+
+def __open_ifcfg(interface):
+ """Open a network interface configuration file.
+
+ Opens the configuration file for interface, writes a header and
+ common options and returns the file object.
+ """
+ fname = "ifcfg-%s" % interface
+ f = ConfigurationFile(fname)
+
+ f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
+ (os.path.basename(f.path()), os.path.basename(sys.argv[0])))
+ f.write("XEMANAGED=yes\n")
+ f.write("DEVICE=%s\n" % interface)
+ f.write("ONBOOT=no\n")
+
+ return f
+
+def open_network_ifcfg(pif):
+ bridge = bridge_name(pif)
+ interface = interface_name(pif)
+ if bridge:
+ return __open_ifcfg(bridge)
+ else:
+ return __open_ifcfg(interface)
+
+
+def open_pif_ifcfg(pif):
+ pifrec = db.get_pif_record(pif)
+
+ log("Configuring %s (%s)" % (interface_name(pif), pifrec['MAC']))
+
+ f = __open_ifcfg(interface_name(pif))
+
+ if pifrec.has_key('other_config'):
+ configure_ethtool(pifrec['other_config'], f)
+ configure_mtu(pifrec['other_config'], f)
+
+ return f
+
+def configure_network(pif, f):
+ """Write the configuration file for a network.
+
+ Writes configuration derived from the network object into the relevant
+ ifcfg file. The configuration file is passed in, but if the network is
+ bridgeless it will be ifcfg-<interface>, otherwise it will be ifcfg-<bridge>.
+
+ This routine may also write ifcfg files of the networks corresponding to other PIFs
+ in order to maintain consistency.
+
+ params:
+ pif: Opaque_ref of pif
+ f : ConfigurationFile(/path/to/ifcfg) to which we append network configuration
+ """
+
+ pifrec = db.get_pif_record(pif)
+ host = pifrec['host']
+ nw = pifrec['network']
+ nwrec = db.get_network_record(nw)
+ oc = None
+ bridge = bridge_name(pif)
+ interface = interface_name(pif)
+ if bridge:
+ device = bridge
+ else:
+ device = interface
+
+ if nwrec.has_key('other_config'):
+ configure_ethtool(nwrec['other_config'], f)
+ configure_mtu(nwrec['other_config'], f)
+ configure_static_routes(device, nwrec['other_config'], f)
+
+
+ if pifrec.has_key('other_config'):
+ oc = pifrec['other_config']
+
+ if device == bridge:
+ f.write("TYPE=Bridge\n")
+ f.write("DELAY=0\n")
+ f.write("STP=off\n")
+ f.write("PIFDEV=%s\n" % interface_name(pif))
+
+ if pifrec['ip_configuration_mode'] == "DHCP":
+ f.write("BOOTPROTO=dhcp\n")
+ f.write("PERSISTENT_DHCLIENT=yes\n")
+ elif pifrec['ip_configuration_mode'] == "Static":
+ f.write("BOOTPROTO=none\n")
+ f.write("NETMASK=%(netmask)s\n" % pifrec)
+ f.write("IPADDR=%(IP)s\n" % pifrec)
+ f.write("GATEWAY=%(gateway)s\n" % pifrec)
+ elif pifrec['ip_configuration_mode'] == "None":
+ f.write("BOOTPROTO=none\n")
+ else:
+ raise Error("Unknown ip-configuration-mode %s" % pifrec['ip_configuration_mode'])
+
+ if pifrec.has_key('DNS') and pifrec['DNS'] != "":
+ ServerList = pifrec['DNS'].split(",")
+ for i in range(len(ServerList)): f.write("DNS%d=%s\n" % (i+1, ServerList[i]))
+ if oc and oc.has_key('domain'):
+ f.write("DOMAIN='%s'\n" % oc['domain'].replace(',', ' '))
+
+ # We only allow one ifcfg-xenbr* to have PEERDNS=yes and there can be only one GATEWAYDEV in /etc/sysconfig/network.
+ # 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.
+
+ # Work out which pif on this host should be the one with PEERDNS=yes and which should be the GATEWAYDEV
+ #
+ # 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.
+ pifs_on_host = [ __pif for __pif in db.get_all_pifs() if
+ db.get_pif_record(__pif)['host'] == host and
+ (not __pif in get_bond_masters_of_pif(pif)) ]
+ other_pifs_on_host = [ __pif for __pif in pifs_on_host if __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_on_host:
+ __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
+
+ # Update all the other network's ifcfg files and ensure consistency
+ for __pif in other_pifs_on_host:
+ __f = open_network_ifcfg(__pif)
+ peerdns_line_wanted = 'PEERDNS=%s\n' % ((__pif == peerdns_pif) and 'yes' or 'no')
+ lines = __f.readlines()
+
+ if not peerdns_line_wanted in lines:
+ # the PIF selected for DNS has changed and as a result this ifcfg file needs rewriting
+ for line in lines:
+ if not line.lstrip().startswith('PEERDNS'):
+ __f.write(line)
+ log("Setting %s in %s" % (peerdns_line_wanted.strip(), __f.path()))
+ __f.write(peerdns_line_wanted)
+ __f.close()
+ f.attach_child(__f)
+
+ else:
+ # There is no need to change this ifcfg file. So don't attach_child.
+ pass
+
+ # ... and for this pif too
+ f.write('PEERDNS=%s\n' % ((pif == peerdns_pif) and 'yes' or 'no'))
+
+ # Update gatewaydev
+ fnetwork = ConfigurationFile("network", "/etc/sysconfig")
+ for line in fnetwork.readlines():
+ if line.lstrip().startswith('GATEWAY') :
+ continue
+ fnetwork.write(line)
+ if defaultroute_pif:
+ gatewaydev = bridge_name(defaultroute_pif)
+ if not gatewaydev:
+ gatewaydev = interface_name(defaultroute_pif)
+ fnetwork.write('GATEWAYDEV=%s\n' % gatewaydev)
+ fnetwork.close()
+ f.attach_child(fnetwork)
+
+ return
+
+
+def configure_physical_interface(pif):
+ """Write the configuration for a physical interface.
+
+ Writes the configuration file for the physical interface described by
+ the pif object.
+
+ Returns the open file handle for the interface configuration file.
+ """
+ pifrec = db.get_pif_record(pif)
+
+ f = open_pif_ifcfg(pif)
+
+ f.write("TYPE=Ethernet\n")
+ f.write("HWADDR=%(MAC)s\n" % pifrec)
+
+ return f
+
+def configure_bond_interface(pif):
+ """Write the configuration for a bond interface.
+
+ Writes the configuration file for the bond interface described by
+ the pif object. Handles writing the configuration for the slave
+ interfaces.
+
+ Returns the open file handle for the bond interface configuration
+ file.
+ """
+
+ pifrec = db.get_pif_record(pif)
+ oc = pifrec['other_config']
+ f = open_pif_ifcfg(pif)
+
+ if pifrec['MAC'] != "":
+ f.write("MACADDR=%s\n" % pifrec['MAC'])
+
+ for slave in get_bond_slaves_of_pif(pif):
+ s = configure_physical_interface(slave)
+ s.write("MASTER=%(device)s\n" % pifrec)
+ s.write("SLAVE=yes\n")
+ s.close()
+ f.attach_child(s)
+
+ # The bond option defaults
+ 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-"
+ overrides = filter(lambda (key,val): key.startswith("bond-"), oc.items())
+ overrides = map(lambda (key,val): (key[5:], val), overrides)
+ bond_options.update(overrides)
+
+ # write the bond options to ifcfg-bondX
+ f.write('BONDING_OPTS="')
+ for (name,val) in bond_options.items():
+ f.write("%s=%s " % (name,val))
+ f.write('"\n')
+ return f
+
+def configure_vlan_interface(pif):
+ """Write the configuration for a VLAN interface.
+
+ Writes the configuration file for the VLAN interface described by
+ the pif object. Handles writing the configuration for the master
+ interface if necessary.
+
+ Returns the open file handle for the VLAN interface configuration
+ file.
+ """
+
+ slave = configure_pif(get_vlan_slave_of_pif(pif))
+ slave.close()
+
+ f = open_pif_ifcfg(pif)
+ f.write("VLAN=yes\n")
+ f.attach_child(slave)
+
+ return f
+
+def configure_pif(pif):
+ """Write the configuration for a PIF object.
+
+ Writes the configuration file the PIF and all dependent
+ interfaces (bond slaves and VLAN masters etc).
+
+ Returns the open file handle for the interface configuration file.
+ """
+
+ pifrec = db.get_pif_record(pif)
+
+ if pifrec['VLAN'] != '-1':
+ f = configure_vlan_interface(pif)
+ elif len(pifrec['bond_master_of']) != 0:
+ f = configure_bond_interface(pif)
+ else:
+ f = configure_physical_interface(pif)
+
+ bridge = bridge_name(pif)
+ if bridge:
+ f.write("BRIDGE=%s\n" % bridge)
+
+ return f
+
+def unconfigure_pif(pif):
+ """Clear up the files created by configure_pif"""
+ f = open_pif_ifcfg(pif)
+ log("Unlinking stale file %s" % f.path())
+ f.unlink()
+ return f
+\f
if __name__ == "__main__":
rc = 1
try: