X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=xenserver%2Fopt_xensource_libexec_InterfaceReconfigure.py;h=f37e038635decf24bcab630218bce36cc7b7675b;hb=HEAD;hp=3847cb65b40bbfdc853717a41bf88855f26f4f2d;hpb=96c7918c4043326bfa0e05b3fc9cbb9547279d40;p=sliver-openvswitch.git diff --git a/xenserver/opt_xensource_libexec_InterfaceReconfigure.py b/xenserver/opt_xensource_libexec_InterfaceReconfigure.py index 3847cb65b..f37e03863 100644 --- a/xenserver/opt_xensource_libexec_InterfaceReconfigure.py +++ b/xenserver/opt_xensource_libexec_InterfaceReconfigure.py @@ -10,18 +10,41 @@ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # +import sys import syslog import os from xml.dom.minidom import getDOMImplementation from xml.dom.minidom import parse as parseXML +the_root_prefix = "" +def root_prefix(): + """Returns a string to prefix to all file name references, which + is useful for testing.""" + return the_root_prefix +def set_root_prefix(prefix): + global the_root_prefix + the_root_prefix = prefix + +log_destination = "syslog" +def get_log_destination(): + """Returns the current log destination. + 'syslog' means "log to syslog". + 'stderr' means "log to stderr".""" + return log_destination +def set_log_destination(dest): + global log_destination + log_destination = dest + # # Logging. # def log(s): - syslog.syslog(s) + if get_log_destination() == 'syslog': + syslog.syslog(s) + else: + print >>sys.stderr, s # # Exceptions. @@ -38,7 +61,7 @@ class Error(Exception): def run_command(command): log("Running command: " + ' '.join(command)) - rc = os.spawnl(os.P_WAIT, command[0], *command) + rc = os.spawnl(os.P_WAIT, root_prefix() + command[0], *command) if rc != 0: log("Command failed %d: " % rc + ' '.join(command)) return False @@ -218,20 +241,27 @@ def _strlist_from_xml(n, ltag, itag): ret.append(_str_from_xml(n)) return ret -def _otherconfig_to_xml(xml, parent, val, attrs): - otherconfig = xml.createElement("other_config") - parent.appendChild(otherconfig) +def _map_to_xml(xml, parent, tag, val, attrs): + e = xml.createElement(tag) + parent.appendChild(e) for n,v in val.items(): - if not n in attrs: - raise Error("Unknown other-config attribute: %s" % n) - _str_to_xml(xml, otherconfig, n, v) -def _otherconfig_from_xml(n, attrs): + if n in attrs: + _str_to_xml(xml, e, n, v) + else: + log("Unknown other-config attribute: %s" % n) + +def _map_from_xml(n, attrs): ret = {} for n in n.childNodes: if n.nodeName in attrs: ret[n.nodeName] = _str_from_xml(n) return ret +def _otherconfig_to_xml(xml, parent, val, attrs): + return _map_to_xml(xml, parent, "other_config", val, attrs) +def _otherconfig_from_xml(n, attrs): + return _map_from_xml(n, attrs) + # # Definitions of the database objects (and their attributes) used by interface-reconfigure. # @@ -244,13 +274,17 @@ def _otherconfig_from_xml(n, attrs): _PIF_XML_TAG = "pif" _VLAN_XML_TAG = "vlan" +_TUNNEL_XML_TAG = "tunnel" _BOND_XML_TAG = "bond" _NETWORK_XML_TAG = "network" +_POOL_XML_TAG = "pool" -_ETHTOOL_OTHERCONFIG_ATTRS = ['ethtool-%s' % x for x in 'autoneg', 'speed', 'duplex', 'rx', 'tx', 'sg', 'tso', 'ufo', 'gso' ] +_ETHTOOL_OTHERCONFIG_ATTRS = ['ethtool-%s' % x for x in 'autoneg', 'speed', 'duplex', 'rx', 'tx', 'sg', 'tso', 'ufo', 'gso', 'gro', 'lro' ] _PIF_OTHERCONFIG_ATTRS = [ 'domain', 'peerdns', 'defaultroute', 'mtu', 'static-routes' ] + \ - [ 'bond-%s' % x for x in 'mode', 'miimon', 'downdelay', 'updelay', 'use_carrier' ] + \ + [ 'bond-%s' % x for x in 'mode', 'miimon', 'downdelay', + 'updelay', 'use_carrier', 'hashing-algorithm' ] + \ + [ 'vlan-bug-workaround' ] + \ _ETHTOOL_OTHERCONFIG_ATTRS _PIF_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), @@ -264,6 +298,10 @@ _PIF_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), 'VLAN_master_of': (_str_to_xml,_str_from_xml), 'VLAN_slave_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'VLAN_slave_of', 'master', v), lambda n: _strlist_from_xml(n, 'VLAN_slave_Of', 'master')), + 'tunnel_access_PIF_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'tunnel_access_PIF_of', 'pif', v), + lambda n: _strlist_from_xml(n, 'tunnel_access_PIF_of', 'pif')), + 'tunnel_transport_PIF_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'tunnel_transport_PIF_of', 'pif', v), + lambda n: _strlist_from_xml(n, 'tunnel_transport_PIF_of', 'pif')), 'ip_configuration_mode': (_str_to_xml,_str_from_xml), 'IP': (_str_to_xml,_str_from_xml), 'netmask': (_str_to_xml,_str_from_xml), @@ -285,22 +323,37 @@ _VLAN_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), 'untagged_PIF': (_str_to_xml,_str_from_xml), } +_TUNNEL_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), + 'access_PIF': (_str_to_xml,_str_from_xml), + 'transport_PIF': (_str_to_xml,_str_from_xml), + } _BOND_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), 'master': (_str_to_xml,_str_from_xml), 'slaves': (lambda x, p, t, v: _strlist_to_xml(x, p, 'slaves', 'slave', v), lambda n: _strlist_from_xml(n, 'slaves', 'slave')), } -_NETWORK_OTHERCONFIG_ATTRS = [ 'mtu', 'static-routes' ] + _ETHTOOL_OTHERCONFIG_ATTRS +_NETWORK_OTHERCONFIG_ATTRS = [ 'mtu', + 'static-routes', + 'vswitch-controller-fail-mode', + 'vswitch-disable-in-band' ] \ + + _ETHTOOL_OTHERCONFIG_ATTRS _NETWORK_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml), 'bridge': (_str_to_xml,_str_from_xml), + 'MTU': (_str_to_xml,_str_from_xml), 'PIFs': (lambda x, p, t, v: _strlist_to_xml(x, p, 'PIFs', 'PIF', v), lambda n: _strlist_from_xml(n, 'PIFs', 'PIF')), 'other_config': (lambda x, p, t, v: _otherconfig_to_xml(x, p, v, _NETWORK_OTHERCONFIG_ATTRS), lambda n: _otherconfig_from_xml(n, _NETWORK_OTHERCONFIG_ATTRS)), } +_POOL_OTHERCONFIG_ATTRS = ['vswitch-controller-fail-mode'] + +_POOL_ATTRS = { 'other_config': (lambda x, p, t, v: _otherconfig_to_xml(x, p, v, _POOL_OTHERCONFIG_ATTRS), + lambda n: _otherconfig_from_xml(n, _POOL_OTHERCONFIG_ATTRS)), + } + # # Database Cache object # @@ -323,7 +376,7 @@ def db_init_from_xenapi(session): class DatabaseCache(object): def __read_xensource_inventory(self): - filename = "/etc/xensource-inventory" + filename = root_prefix() + "/etc/xensource-inventory" f = open(filename, "r") lines = [x.strip("\n") for x in f.readlines()] f.close() @@ -351,18 +404,26 @@ class DatabaseCache(object): def __get_vlan_records_from_xapi(self, session): self.__vlans = {} - for v in session.xenapi.VLAN.get_all(): - rec = session.xenapi.VLAN.get_record(v) + for (v,rec) in session.xenapi.VLAN.get_all_records().items(): if not self.__pif_on_host(rec['untagged_PIF']): continue self.__vlans[v] = {} for f in _VLAN_ATTRS: self.__vlans[v][f] = rec[f] + def __get_tunnel_records_from_xapi(self, session): + self.__tunnels = {} + for t in session.xenapi.tunnel.get_all(): + rec = session.xenapi.tunnel.get_record(t) + if not self.__pif_on_host(rec['transport_PIF']): + continue + self.__tunnels[t] = {} + for f in _TUNNEL_ATTRS: + self.__tunnels[t][f] = rec[f] + def __get_bond_records_from_xapi(self, session): self.__bonds = {} - for b in session.xenapi.Bond.get_all(): - rec = session.xenapi.Bond.get_record(b) + for (b,rec) in session.xenapi.Bond.get_all_records().items(): if not self.__pif_on_host(rec['master']): continue self.__bonds[b] = {} @@ -371,13 +432,16 @@ class DatabaseCache(object): def __get_network_records_from_xapi(self, session): self.__networks = {} - for n in session.xenapi.network.get_all(): - rec = session.xenapi.network.get_record(n) + for (n,rec) in session.xenapi.network.get_all_records().items(): self.__networks[n] = {} for f in _NETWORK_ATTRS: if f == "PIFs": # drop PIFs on other hosts self.__networks[n][f] = [p for p in rec[f] if self.__pif_on_host(p)] + elif f == "MTU" and f not in rec: + # XenServer 5.5 network records did not have an + # MTU field, so allow this to be missing. + pass else: self.__networks[n][f] = rec[f] self.__networks[n]['other_config'] = {} @@ -385,6 +449,20 @@ class DatabaseCache(object): if not rec['other_config'].has_key(f): continue self.__networks[n]['other_config'][f] = rec['other_config'][f] + def __get_pool_records_from_xapi(self, session): + self.__pools = {} + for p in session.xenapi.pool.get_all(): + rec = session.xenapi.pool.get_record(p) + + self.__pools[p] = {} + + for f in _POOL_ATTRS: + self.__pools[p][f] = rec[f] + + for f in _POOL_OTHERCONFIG_ATTRS: + if rec['other_config'].has_key(f): + self.__pools[p]['other_config'][f] = rec['other_config'][f] + def __to_xml(self, xml, parent, key, ref, rec, attrs): """Encode a database object as XML""" e = xml.createElement(key) @@ -430,7 +508,8 @@ class DatabaseCache(object): host = session.xenapi.host.get_by_uuid(inventory['INSTALLATION_UUID']) self.__get_pif_records_from_xapi(session, host) - + self.__get_pool_records_from_xapi(session) + self.__get_tunnel_records_from_xapi(session) self.__get_vlan_records_from_xapi(session) self.__get_bond_records_from_xapi(session) self.__get_network_records_from_xapi(session) @@ -440,11 +519,13 @@ class DatabaseCache(object): else: log("Loading xapi database cache from %s" % cache_file) - xml = parseXML(cache_file) + xml = parseXML(root_prefix() + cache_file) self.__pifs = {} self.__bonds = {} self.__vlans = {} + self.__pools = {} + self.__tunnels = {} self.__networks = {} assert(len(xml.childNodes) == 1) @@ -464,9 +545,15 @@ class DatabaseCache(object): elif n.nodeName == _VLAN_XML_TAG: (ref,rec) = self.__from_xml(n, _VLAN_ATTRS) self.__vlans[ref] = rec + elif n.nodeName == _TUNNEL_XML_TAG: + (ref,rec) = self.__from_xml(n, _TUNNEL_ATTRS) + self.__vlans[ref] = rec elif n.nodeName == _NETWORK_XML_TAG: (ref,rec) = self.__from_xml(n, _NETWORK_ATTRS) self.__networks[ref] = rec + elif n.nodeName == _POOL_XML_TAG: + (ref,rec) = self.__from_xml(n, _POOL_ATTRS) + self.__pools[ref] = rec else: raise Error("Unknown XML element %s" % n.nodeName) @@ -480,13 +567,19 @@ class DatabaseCache(object): self.__to_xml(xml, xml.documentElement, _BOND_XML_TAG, ref, rec, _BOND_ATTRS) for (ref,rec) in self.__vlans.items(): self.__to_xml(xml, xml.documentElement, _VLAN_XML_TAG, ref, rec, _VLAN_ATTRS) + for (ref,rec) in self.__tunnels.items(): + self.__to_xml(xml, xml.documentElement, _TUNNEL_XML_TAG, ref, rec, _TUNNEL_ATTRS) for (ref,rec) in self.__networks.items(): self.__to_xml(xml, xml.documentElement, _NETWORK_XML_TAG, ref, rec, _NETWORK_ATTRS) + for (ref,rec) in self.__pools.items(): + self.__to_xml(xml, xml.documentElement, _POOL_XML_TAG, ref, rec, _POOL_ATTRS) - f = open(cache_file, 'w') + temp_file = cache_file + ".%d" % os.getpid() + f = open(temp_file, 'w') f.write(xml.toprettyxml()) f.close() + os.rename(temp_file, cache_file) def get_pif_by_uuid(self, uuid): pifs = map(lambda (ref,rec): ref, @@ -504,10 +597,21 @@ class DatabaseCache(object): filter(lambda (ref,rec): rec['device'] == device, self.__pifs.items())) + def get_networks_with_bridge(self, bridge): + return map(lambda (ref,rec): ref, + filter(lambda (ref,rec): rec['bridge'] == bridge, + self.__networks.items())) + + def get_network_by_bridge(self, bridge): + #Assumes one network has bridge. + try: + return self.get_networks_with_bridge(bridge)[0] + except KeyError: + return None + def get_pif_by_bridge(self, bridge): - networks = map(lambda (ref,rec): ref, - filter(lambda (ref,rec): rec['bridge'] == bridge, - self.__networks.items())) + networks = self.get_networks_with_bridge(bridge) + if len(networks) == 0: raise Error("No matching network \"%s\"" % bridge) @@ -558,11 +662,16 @@ class DatabaseCache(object): else: return None + def get_pool_record(self): + if len(self.__pools) > 0: + return self.__pools.values()[0] + # # # +PIF_OTHERCONFIG_DEFAULTS = {'gro': 'off', 'lro': 'off'} -def ethtool_settings(oc): +def ethtool_settings(oc, defaults = {}): settings = [] if oc.has_key('ethtool-speed'): val = oc['ethtool-speed'] @@ -572,8 +681,8 @@ def ethtool_settings(oc): 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'] + if val in ["half", "full"]: + settings += ['duplex', val] else: log("Invalid value for ethtool-duplex = %s. Must be half|full." % val) if oc.has_key('ethtool-autoneg'): @@ -585,7 +694,7 @@ def ethtool_settings(oc): else: log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val) offload = [] - for opt in ("rx", "tx", "sg", "tso", "ufo", "gso"): + for opt in ("rx", "tx", "sg", "tso", "ufo", "gso", "gro", "lro"): if oc.has_key("ethtool-" + opt): val = oc["ethtool-" + opt] if val in ["true", "on"]: @@ -594,15 +703,37 @@ def ethtool_settings(oc): offload += [opt, 'off'] else: log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val)) + elif opt in defaults: + offload += [opt, defaults[opt]] return settings,offload -def mtu_setting(oc): +# By default the MTU is taken from the Network.MTU setting for VIF, +# PIF and Bridge. However it is possible to override this by using +# {VIF,PIF,Network}.other-config:mtu. +# +# type parameter is a string describing the object that the oc parameter +# is from. e.g. "PIF", "Network" +def mtu_setting(nw, type, oc): + mtu = None + + nwrec = db().get_network_record(nw) + if nwrec.has_key('MTU'): + mtu = nwrec['MTU'] + else: + mtu = "1500" + if oc.has_key('mtu'): + log("Override Network.MTU setting on bridge %s from %s.MTU is %s" % \ + (nwrec['bridge'], type, mtu)) + mtu = oc['mtu'] + + if mtu is not None: try: - int(oc['mtu']) # Check that the value is an integer - return oc['mtu'] + int(mtu) # Check that the value is an integer + return mtu except ValueError, x: - log("Invalid value for mtu = %s" % oc['mtu']) + log("Invalid value for mtu = %s" % mtu) + return None # @@ -625,7 +756,7 @@ def pif_ipdev_name(pif): # def netdev_exists(netdev): - return os.path.exists("/sys/class/net/" + netdev) + return os.path.exists(root_prefix() + "/sys/class/net/" + netdev) def pif_netdev_name(pif): """Get the netdev name for a PIF.""" @@ -742,6 +873,12 @@ def pif_get_vlan_masters(pif): vlans = [db().get_vlan_record(v) for v in pifrec['VLAN_slave_of']] return [v['untagged_PIF'] for v in vlans if v and db().pif_exists(v['untagged_PIF'])] +# +# Tunnel PIFs +# +def pif_is_tunnel(pif): + return len(db().get_pif_record(pif)['tunnel_access_PIF_of']) > 0 + # # Datapath base class # @@ -755,6 +892,12 @@ class Datapath(object): def __init__(self, pif): self._pif = pif + @classmethod + def rewrite(cls): + """Class method called when write action is called. Can be used + to update any backend specific configuration.""" + pass + def configure_ipdev(self, cfg): """Write ifcfg TYPE field for an IPdev, plus any type specific fields to cfg @@ -780,7 +923,7 @@ class Datapath(object): Should assume any configuration files changed attached in the preconfigure stage are applied and bring up the - necesary devices to provide the datapath for the + necessary devices to provide the datapath for the PIF. Should not bring up the IPdev. @@ -802,11 +945,11 @@ class Datapath(object): """ raise NotImplementedError -def DatapathFactory(pif): +def DatapathFactory(): # XXX Need a datapath object for bridgeless PIFs try: - network_conf = open("/etc/xensource/network.conf", 'r') + network_conf = open(root_prefix() + "/etc/xensource/network.conf", 'r') network_backend = network_conf.readline().strip() network_conf.close() except Exception, e: @@ -814,9 +957,9 @@ def DatapathFactory(pif): if network_backend == "bridge": from InterfaceReconfigureBridge import DatapathBridge - return DatapathBridge(pif) - elif network_backend == "vswitch": + return DatapathBridge + elif network_backend in ["openvswitch", "vswitch"]: from InterfaceReconfigureVswitch import DatapathVswitch - return DatapathVswitch(pif) + return DatapathVswitch else: raise Error("unknown network backend %s" % network_backend)