1 # Copyright (c) 2008,2009 Citrix Systems, Inc.
2 # Copyright (c) 2009,2010 Nicira Networks.
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU Lesser General Public License as published
6 # by the Free Software Foundation; version 2.1 only. with the special
7 # exception on linking described in file LICENSE.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Lesser General Public License for more details.
14 from InterfaceReconfigure import *
17 # Bare Network Devices -- network devices without IP configuration
20 def netdev_down(netdev):
21 """Bring down a bare network device"""
22 if not netdev_exists(netdev):
23 log("netdev: down: device %s does not exist, ignoring" % netdev)
25 run_command(["/sbin/ifconfig", netdev, 'down'])
27 def netdev_up(netdev, mtu=None):
28 """Bring up a bare network device"""
29 if not netdev_exists(netdev):
30 raise Error("netdev: up: device %s does not exist" % netdev)
37 run_command(["/sbin/ifconfig", netdev, 'up'] + mtu)
43 def pif_bridge_name(pif):
44 """Return the bridge name of a pif.
46 PIF must not be a VLAN and must be a bridged PIF."""
48 pifrec = db().get_pif_record(pif)
51 raise Error("PIF %(uuid)s cannot be a bridge, VLAN is %(VLAN)s" % pifrec)
53 nwrec = db().get_network_record(pifrec['network'])
56 return nwrec['bridge']
58 raise Error("PIF %(uuid)s does not have a bridge name" % pifrec)
64 def pif_currently_in_use(pif):
65 """Determine if a PIF is currently in use.
67 A PIF is determined to be currently in use if
68 - PIF.currently-attached is true
69 - Any bond master is currently attached
70 - Any VLAN master is currently attached
72 rec = db().get_pif_record(pif)
73 if rec['currently_attached']:
74 log("configure_datapath: %s is currently attached" % (pif_netdev_name(pif)))
76 for b in pif_get_bond_masters(pif):
77 if pif_currently_in_use(b):
78 log("configure_datapath: %s is in use by BOND master %s" % (pif_netdev_name(pif),pif_netdev_name(b)))
80 for v in pif_get_vlan_masters(pif):
81 if pif_currently_in_use(v):
82 log("configure_datapath: %s is in use by VLAN master %s" % (pif_netdev_name(pif),pif_netdev_name(v)))
87 # Datapath Configuration
90 def pif_datapath(pif):
91 """Return the datapath PIF associated with PIF.
92 A non-VLAN PIF is its own datapath PIF, except that a bridgeless PIF has
93 no datapath PIF at all.
94 A VLAN PIF's datapath PIF is its VLAN slave's datapath PIF.
97 return pif_datapath(pif_get_vlan_slave(pif))
99 pifrec = db().get_pif_record(pif)
100 nwrec = db().get_network_record(pifrec['network'])
101 if not nwrec['bridge']:
106 def datapath_get_physical_pifs(pif):
107 """Return the PIFs for the physical network device(s) associated with a datapath PIF.
108 For a bond master PIF, these are the bond slave PIFs.
109 For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
111 A VLAN PIF cannot be a datapath PIF.
114 # Seems like overkill...
115 raise Error("get-physical-pifs should not get passed a VLAN")
116 elif pif_is_bond(pif):
117 return pif_get_bond_slaves(pif)
121 def datapath_deconfigure_physical(netdev):
122 return ['--', '--with-iface', '--if-exists', 'del-port', netdev]
124 def datapath_configure_bond(pif,slaves):
125 bridge = pif_bridge_name(pif)
126 pifrec = db().get_pif_record(pif)
127 interface = pif_netdev_name(pif)
129 argv = ['--', '--fake-iface', 'add-bond', bridge, interface]
131 argv += [pif_netdev_name(slave)]
133 # XXX need ovs-vsctl support
134 #if pifrec['MAC'] != "":
135 # argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
139 "mode": "balance-slb",
145 # override defaults with values from other-config whose keys
147 oc = pifrec['other_config']
148 overrides = filter(lambda (key,val):
149 key.startswith("bond-"), oc.items())
150 overrides = map(lambda (key,val): (key[5:], val), overrides)
151 bond_options.update(overrides)
152 for (name,val) in bond_options.items():
153 # XXX need ovs-vsctl support for bond options
154 #argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
158 def datapath_deconfigure_bond(netdev):
159 return ['--', '--with-iface', '--if-exists', 'del-port', netdev]
161 def datapath_deconfigure_ipdev(interface):
162 return ['--', '--with-iface', '--if-exists', 'del-port', interface]
164 def datapath_modify_config(commands):
165 #log("modifying configuration:")
169 rc = run_command(['/usr/bin/ovs-vsctl'] + ['--timeout=20']
170 + [c for c in commands if not c.startswith('#')])
172 raise Error("Failed to modify vswitch configuration")
176 # Toplevel Datapath Configuration.
179 def configure_datapath(pif, parent=None, vlan=None):
180 """Bring up the datapath configuration for PIF.
182 Should be careful not to glitch existing users of the datapath, e.g. other VLANs etc.
184 Should take care of tearing down other PIFs which encompass common physical devices.
186 Returns a tuple containing
187 - A list containing the necessary vsctl command line arguments
188 - A list of additional devices which should be brought up after
189 the configuration is applied.
195 bridge = pif_bridge_name(pif)
197 physical_devices = datapath_get_physical_pifs(pif)
199 # Determine additional devices to deconfigure.
201 # Given all physical devices which are part of this PIF we need to
203 # - any additional bond which a physical device is part of.
204 # - any additional physical devices which are part of an additional bond.
206 # Any of these which are not currently in use should be brought
207 # down and deconfigured.
208 extra_down_bonds = []
209 extra_down_ports = []
210 for p in physical_devices:
211 for bond in pif_get_bond_masters(p):
213 log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond))
215 if bond in extra_down_bonds:
217 if db().get_pif_record(bond)['currently_attached']:
218 log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond))
220 extra_down_bonds += [bond]
222 for s in pif_get_bond_slaves(bond):
223 if s in physical_devices:
225 if s in extra_down_ports:
227 if pif_currently_in_use(s):
229 extra_down_ports += [s]
231 log("configure_datapath: bridge - %s" % bridge)
232 log("configure_datapath: physical - %s" % [pif_netdev_name(p) for p in physical_devices])
233 log("configure_datapath: extra ports - %s" % [pif_netdev_name(p) for p in extra_down_ports])
234 log("configure_datapath: extra bonds - %s" % [pif_netdev_name(p) for p in extra_down_bonds])
236 # Need to fully deconfigure any bridge which any of the:
241 for brpif in physical_devices + extra_down_ports + extra_down_bonds:
244 b = pif_bridge_name(brpif)
248 vsctl_argv += ['# remove bridge %s' % b]
249 vsctl_argv += ['--', '--if-exists', 'del-br', b]
251 for n in extra_down_ports:
252 dev = pif_netdev_name(n)
253 vsctl_argv += ['# deconfigure sibling physical device %s' % dev]
254 vsctl_argv += datapath_deconfigure_physical(dev)
257 for n in extra_down_bonds:
258 dev = pif_netdev_name(n)
259 vsctl_argv += ['# deconfigure bond device %s' % dev]
260 vsctl_argv += datapath_deconfigure_bond(dev)
263 for p in physical_devices:
264 dev = pif_netdev_name(p)
265 vsctl_argv += ['# deconfigure physical port %s' % dev]
266 vsctl_argv += datapath_deconfigure_physical(dev)
268 if parent and datapath:
269 vsctl_argv += ['--', '--may-exist', 'add-br', bridge, parent, vlan]
271 vsctl_argv += ['--', '--may-exist', 'add-br', bridge]
273 if len(physical_devices) > 1:
274 vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
275 vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
276 vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)]
277 vsctl_argv += datapath_configure_bond(pif, physical_devices)
278 extra_up_ports += [pif_netdev_name(pif)]
280 iface = pif_netdev_name(physical_devices[0])
281 vsctl_argv += ['# add physical device %s' % iface]
282 vsctl_argv += ['--', '--may-exist', 'add-port', bridge, iface]
284 return vsctl_argv,extra_up_ports
286 def deconfigure_datapath(pif):
289 bridge = pif_bridge_name(pif)
291 physical_devices = datapath_get_physical_pifs(pif)
293 log("deconfigure_datapath: bridge - %s" % bridge)
294 log("deconfigure_datapath: physical devices - %s" % [pif_netdev_name(p) for p in physical_devices])
296 for p in physical_devices:
297 dev = pif_netdev_name(p)
298 vsctl_argv += ['# deconfigure physical port %s' % dev]
299 vsctl_argv += datapath_deconfigure_physical(dev)
302 if len(physical_devices) > 1:
303 vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
304 vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
306 vsctl_argv += ['# deconfigure bridge %s' % bridge]
307 vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
315 class DatapathVswitch(Datapath):
316 def __init__(self, pif):
317 Datapath.__init__(self, pif)
318 self._dp = pif_datapath(pif)
319 self._ipdev = pif_ipdev_name(pif)
321 if pif_is_vlan(pif) and not self._dp:
322 raise Error("Unbridged VLAN devices not implemented yet")
324 log("Configured for Vswitch datapath")
326 def configure_ipdev(self, cfg):
327 cfg.write("TYPE=Ethernet\n")
329 def preconfigure(self, parent):
333 pifrec = db().get_pif_record(self._pif)
334 dprec = db().get_pif_record(self._dp)
337 bridge = pif_bridge_name(self._dp)
338 if pif_is_vlan(self._pif):
339 datapath = pif_datapath(self._pif)
340 c,e = configure_datapath(self._dp, datapath, pifrec['VLAN'])
342 c,e = configure_datapath(self._dp)
346 xs_network_uuids = []
347 for nwpif in db().get_pifs_by_device(db().get_pif_record(self._pif)['device']):
348 rec = db().get_pif_record(nwpif)
350 # When state is read from dbcache PIF.currently_attached
351 # is always assumed to be false... Err on the side of
352 # listing even detached networks for the time being.
353 #if nwpif != pif and not rec['currently_attached']:
354 # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
356 nwrec = db().get_network_record(rec['network'])
357 xs_network_uuids += [nwrec['uuid']]
359 vsctl_argv += ['# configure xs-network-uuids']
360 vsctl_argv += ['--', 'br-set-external-id', bridge,
361 'xs-network-uuids', ';'.join(xs_network_uuids)]
364 vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
365 vsctl_argv += datapath_deconfigure_ipdev(ipdev)
366 vsctl_argv += ["# reconfigure ipdev %s" % ipdev]
367 vsctl_argv += ['--', 'add-port', bridge, ipdev]
369 # XXX Needs support in ovs-vsctl
371 # vsctl_argv += ['--add=bridge.%s.mac=%s' % (bridge, dprec['MAC'])]
373 # vsctl_argv += ['--add=iface.%s.mac=%s' % (ipdev, dprec['MAC'])]
375 self._vsctl_argv = vsctl_argv
376 self._extra_ports = extra_ports
378 def bring_down_existing(self):
382 # Bring up physical devices. ovs-vswitchd initially enables or
383 # disables bond slaves based on whether carrier is detected
384 # when they are added, and a network device that is down
385 # always reports "no carrier".
386 physical_devices = datapath_get_physical_pifs(self._dp)
388 for p in physical_devices:
389 oc = db().get_pif_record(p)['other_config']
391 dev = pif_netdev_name(p)
393 mtu = mtu_setting(oc)
397 settings, offload = ethtool_settings(oc)
399 run_command(['/sbin/ethtool', '-s', dev] + settings)
401 run_command(['/sbin/ethtool', '-K', dev] + offload)
403 datapath_modify_config(self._vsctl_argv)
406 for p in self._extra_ports:
407 log("action_up: bring up %s" % p)
410 def bring_down(self):
416 bridge = pif_bridge_name(dp)
418 #nw = db().get_pif_record(self._pif)['network']
419 #nwrec = db().get_network_record(nw)
420 #vsctl_argv += ['# deconfigure xs-network-uuids']
421 #vsctl_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])]
423 log("deconfigure ipdev %s on %s" % (ipdev,bridge))
424 vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
425 vsctl_argv += datapath_deconfigure_ipdev(ipdev)
427 if pif_is_vlan(self._pif):
428 # If the VLAN's slave is attached, leave datapath setup.
429 slave = pif_get_vlan_slave(self._pif)
430 if db().get_pif_record(slave)['currently_attached']:
431 log("action_down: vlan slave is currently attached")
434 # If the VLAN's slave has other VLANs that are attached, leave datapath setup.
435 for master in pif_get_vlan_masters(slave):
436 if master != self._pif and db().get_pif_record(master)['currently_attached']:
437 log("action_down: vlan slave has other master: %s" % pif_netdev_name(master))
440 # Otherwise, take down the datapath too (fall through)
442 log("action_down: no more masters, bring down slave %s" % bridge)
444 # Stop here if this PIF has attached VLAN masters.
445 masters = [db().get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(self._pif) if db().get_pif_record(m)['currently_attached']]
447 log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters))
451 vsctl_argv += deconfigure_datapath(dp)
452 datapath_modify_config(vsctl_argv)