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 be a bridged PIF."""
48 pifrec = db().get_pif_record(pif)
49 nwrec = db().get_network_record(pifrec['network'])
51 return nwrec['bridge']
53 raise Error("PIF %(uuid)s does not have a bridge name" % pifrec)
59 def pif_currently_in_use(pif):
60 """Determine if a PIF is currently in use.
62 A PIF is determined to be currently in use if
63 - PIF.currently-attached is true
64 - Any bond master is currently attached
65 - Any VLAN master is currently attached
67 rec = db().get_pif_record(pif)
68 if rec['currently_attached']:
69 log("configure_datapath: %s is currently attached" % (pif_netdev_name(pif)))
71 for b in pif_get_bond_masters(pif):
72 if pif_currently_in_use(b):
73 log("configure_datapath: %s is in use by BOND master %s" % (pif_netdev_name(pif),pif_netdev_name(b)))
75 for v in pif_get_vlan_masters(pif):
76 if pif_currently_in_use(v):
77 log("configure_datapath: %s is in use by VLAN master %s" % (pif_netdev_name(pif),pif_netdev_name(v)))
82 # Datapath Configuration
85 def pif_datapath(pif):
86 """Return the datapath PIF associated with PIF.
87 A non-VLAN PIF is its own datapath PIF, except that a bridgeless PIF has
88 no datapath PIF at all.
89 A VLAN PIF's datapath PIF is its VLAN slave's datapath PIF.
92 return pif_datapath(pif_get_vlan_slave(pif))
94 pifrec = db().get_pif_record(pif)
95 nwrec = db().get_network_record(pifrec['network'])
96 if not nwrec['bridge']:
101 def datapath_get_physical_pifs(pif):
102 """Return the PIFs for the physical network device(s) associated with a datapath PIF.
103 For a bond master PIF, these are the bond slave PIFs.
104 For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
106 A VLAN PIF cannot be a datapath PIF.
109 # Seems like overkill...
110 raise Error("get-physical-pifs should not get passed a VLAN")
111 elif pif_is_bond(pif):
112 return pif_get_bond_slaves(pif)
116 def datapath_deconfigure_physical(netdev):
117 return ['--', '--with-iface', '--if-exists', 'del-port', netdev]
119 def datapath_configure_bond(pif,slaves):
120 bridge = pif_bridge_name(pif)
121 pifrec = db().get_pif_record(pif)
122 interface = pif_netdev_name(pif)
124 argv = ['--', '--fake-iface', 'add-bond', bridge, interface]
126 argv += [pif_netdev_name(slave)]
128 # XXX need ovs-vsctl support
129 #if pifrec['MAC'] != "":
130 # argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
134 "mode": "balance-slb",
140 # override defaults with values from other-config whose keys
142 oc = pifrec['other_config']
143 overrides = filter(lambda (key,val):
144 key.startswith("bond-"), oc.items())
145 overrides = map(lambda (key,val): (key[5:], val), overrides)
146 bond_options.update(overrides)
147 for (name,val) in bond_options.items():
148 # XXX need ovs-vsctl support for bond options
149 #argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
153 def datapath_deconfigure_bond(netdev):
154 return ['--', '--with-iface', '--if-exists', 'del-port', netdev]
156 def datapath_deconfigure_ipdev(interface):
157 return ['--', '--with-iface', '--if-exists', 'del-port', interface]
159 def datapath_modify_config(commands):
160 #log("modifying configuration:")
164 rc = run_command(['/usr/bin/ovs-vsctl'] + ['--timeout=20']
165 + [c for c in commands if not c.startswith('#')])
167 raise Error("Failed to modify vswitch configuration")
171 # Toplevel Datapath Configuration.
174 def configure_datapath(pif, parent=None, vlan=None):
175 """Bring up the datapath configuration for PIF.
177 Should be careful not to glitch existing users of the datapath, e.g. other VLANs etc.
179 Should take care of tearing down other PIFs which encompass common physical devices.
181 Returns a tuple containing
182 - A list containing the necessary vsctl command line arguments
183 - A list of additional devices which should be brought up after
184 the configuration is applied.
190 bridge = pif_bridge_name(pif)
192 physical_devices = datapath_get_physical_pifs(pif)
194 # Determine additional devices to deconfigure.
196 # Given all physical devices which are part of this PIF we need to
198 # - any additional bond which a physical device is part of.
199 # - any additional physical devices which are part of an additional bond.
201 # Any of these which are not currently in use should be brought
202 # down and deconfigured.
203 extra_down_bonds = []
204 extra_down_ports = []
205 for p in physical_devices:
206 for bond in pif_get_bond_masters(p):
208 log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond))
210 if bond in extra_down_bonds:
212 if db().get_pif_record(bond)['currently_attached']:
213 log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond))
215 extra_down_bonds += [bond]
217 for s in pif_get_bond_slaves(bond):
218 if s in physical_devices:
220 if s in extra_down_ports:
222 if pif_currently_in_use(s):
224 extra_down_ports += [s]
226 log("configure_datapath: bridge - %s" % bridge)
227 log("configure_datapath: physical - %s" % [pif_netdev_name(p) for p in physical_devices])
228 log("configure_datapath: extra ports - %s" % [pif_netdev_name(p) for p in extra_down_ports])
229 log("configure_datapath: extra bonds - %s" % [pif_netdev_name(p) for p in extra_down_bonds])
231 # Need to fully deconfigure any bridge which any of the:
236 for brpif in physical_devices + extra_down_ports + extra_down_bonds:
239 b = pif_bridge_name(brpif)
243 vsctl_argv += ['# remove bridge %s' % b]
244 vsctl_argv += ['--', '--if-exists', 'del-br', b]
246 for n in extra_down_ports:
247 dev = pif_netdev_name(n)
248 vsctl_argv += ['# deconfigure sibling physical device %s' % dev]
249 vsctl_argv += datapath_deconfigure_physical(dev)
252 for n in extra_down_bonds:
253 dev = pif_netdev_name(n)
254 vsctl_argv += ['# deconfigure bond device %s' % dev]
255 vsctl_argv += datapath_deconfigure_bond(dev)
258 for p in physical_devices:
259 dev = pif_netdev_name(p)
260 vsctl_argv += ['# deconfigure physical port %s' % dev]
261 vsctl_argv += datapath_deconfigure_physical(dev)
263 if parent and datapath:
264 vsctl_argv += ['--', '--may-exist', 'add-br', bridge, parent, vlan]
266 vsctl_argv += ['--', '--may-exist', 'add-br', bridge]
268 if len(physical_devices) > 1:
269 vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
270 vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
271 vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)]
272 vsctl_argv += datapath_configure_bond(pif, physical_devices)
273 extra_up_ports += [pif_netdev_name(pif)]
275 iface = pif_netdev_name(physical_devices[0])
276 vsctl_argv += ['# add physical device %s' % iface]
277 vsctl_argv += ['--', '--may-exist', 'add-port', bridge, iface]
279 return vsctl_argv,extra_up_ports
281 def deconfigure_datapath(pif):
284 bridge = pif_bridge_name(pif)
286 physical_devices = datapath_get_physical_pifs(pif)
288 log("deconfigure_datapath: bridge - %s" % bridge)
289 log("deconfigure_datapath: physical devices - %s" % [pif_netdev_name(p) for p in physical_devices])
291 for p in physical_devices:
292 dev = pif_netdev_name(p)
293 vsctl_argv += ['# deconfigure physical port %s' % dev]
294 vsctl_argv += datapath_deconfigure_physical(dev)
297 if len(physical_devices) > 1:
298 vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
299 vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
301 vsctl_argv += ['# deconfigure bridge %s' % bridge]
302 vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
310 class DatapathVswitch(Datapath):
311 def __init__(self, pif):
312 Datapath.__init__(self, pif)
313 self._dp = pif_datapath(pif)
314 self._ipdev = pif_ipdev_name(pif)
316 if pif_is_vlan(pif) and not self._dp:
317 raise Error("Unbridged VLAN devices not implemented yet")
319 log("Configured for Vswitch datapath")
321 def configure_ipdev(self, cfg):
322 cfg.write("TYPE=Ethernet\n")
324 def preconfigure(self, parent):
328 pifrec = db().get_pif_record(self._pif)
329 dprec = db().get_pif_record(self._dp)
332 bridge = pif_bridge_name(self._dp)
333 if pif_is_vlan(self._pif):
334 datapath = pif_datapath(self._pif)
335 c,e = configure_datapath(self._dp, datapath, pifrec['VLAN'])
337 c,e = configure_datapath(self._dp)
341 xs_network_uuids = []
342 for nwpif in db().get_pifs_by_device(db().get_pif_record(self._pif)['device']):
343 rec = db().get_pif_record(nwpif)
345 # When state is read from dbcache PIF.currently_attached
346 # is always assumed to be false... Err on the side of
347 # listing even detached networks for the time being.
348 #if nwpif != pif and not rec['currently_attached']:
349 # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
351 nwrec = db().get_network_record(rec['network'])
352 xs_network_uuids += [nwrec['uuid']]
354 vsctl_argv += ['# configure xs-network-uuids']
355 vsctl_argv += ['--', 'br-set-external-id', bridge,
356 'xs-network-uuids', ';'.join(xs_network_uuids)]
359 vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
360 vsctl_argv += datapath_deconfigure_ipdev(ipdev)
361 vsctl_argv += ["# reconfigure ipdev %s" % ipdev]
362 vsctl_argv += ['--', 'add-port', bridge, ipdev]
364 # XXX Needs support in ovs-vsctl
366 # vsctl_argv += ['--add=bridge.%s.mac=%s' % (bridge, dprec['MAC'])]
368 # vsctl_argv += ['--add=iface.%s.mac=%s' % (ipdev, dprec['MAC'])]
370 self._vsctl_argv = vsctl_argv
371 self._extra_ports = extra_ports
373 def bring_down_existing(self):
377 # Bring up physical devices. ovs-vswitchd initially enables or
378 # disables bond slaves based on whether carrier is detected
379 # when they are added, and a network device that is down
380 # always reports "no carrier".
381 physical_devices = datapath_get_physical_pifs(self._dp)
383 for p in physical_devices:
384 oc = db().get_pif_record(p)['other_config']
386 dev = pif_netdev_name(p)
388 mtu = mtu_setting(oc)
392 settings, offload = ethtool_settings(oc)
394 run_command(['/sbin/ethtool', '-s', dev] + settings)
396 run_command(['/sbin/ethtool', '-K', dev] + offload)
398 datapath_modify_config(self._vsctl_argv)
401 for p in self._extra_ports:
402 log("action_up: bring up %s" % p)
405 def bring_down(self):
411 bridge = pif_bridge_name(dp)
413 #nw = db().get_pif_record(self._pif)['network']
414 #nwrec = db().get_network_record(nw)
415 #vsctl_argv += ['# deconfigure xs-network-uuids']
416 #vsctl_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])]
418 log("deconfigure ipdev %s on %s" % (ipdev,bridge))
419 vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
420 vsctl_argv += datapath_deconfigure_ipdev(ipdev)
422 if pif_is_vlan(self._pif):
423 # If the VLAN's slave is attached, leave datapath setup.
424 slave = pif_get_vlan_slave(self._pif)
425 if db().get_pif_record(slave)['currently_attached']:
426 log("action_down: vlan slave is currently attached")
429 # If the VLAN's slave has other VLANs that are attached, leave datapath setup.
430 for master in pif_get_vlan_masters(slave):
431 if master != self._pif and db().get_pif_record(master)['currently_attached']:
432 log("action_down: vlan slave has other master: %s" % pif_netdev_name(master))
435 # Otherwise, take down the datapath too (fall through)
437 log("action_down: no more masters, bring down slave %s" % bridge)
439 # Stop here if this PIF has attached VLAN masters.
440 masters = [db().get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(self._pif) if db().get_pif_record(m)['currently_attached']]
442 log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters))
446 vsctl_argv += deconfigure_datapath(dp)
447 datapath_modify_config(vsctl_argv)