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 For a non-VLAN PIF, the datapath name is the bridge name.
93 For a VLAN PIF, the datapath name is the bridge name for the PIF's VLAN slave.
96 return pif_datapath(pif_get_vlan_slave(pif))
98 pifrec = db().get_pif_record(pif)
99 nwrec = db().get_network_record(pifrec['network'])
100 if not nwrec['bridge']:
105 def datapath_get_physical_pifs(pif):
106 """Return the PIFs for the physical network device(s) associated with a datapath PIF.
107 For a bond master PIF, these are the bond slave PIFs.
108 For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
110 A VLAN PIF cannot be a datapath PIF.
113 # Seems like overkill...
114 raise Error("get-physical-pifs should not get passed a VLAN")
115 elif pif_is_bond(pif):
116 return pif_get_bond_slaves(pif)
120 def datapath_deconfigure_physical(netdev):
121 return ['--', '--if-exists', 'del-port', netdev]
123 def datapath_configure_bond(pif,slaves):
124 bridge = pif_bridge_name(pif)
125 pifrec = db().get_pif_record(pif)
126 interface = pif_netdev_name(pif)
128 argv = ['--', '--fake-iface', 'add-bond', bridge, interface]
130 argv += [pif_netdev_name(slave)]
132 # XXX need ovs-vsctl support
133 #if pifrec['MAC'] != "":
134 # argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
138 "mode": "balance-slb",
144 # override defaults with values from other-config whose keys
146 oc = pifrec['other_config']
147 overrides = filter(lambda (key,val):
148 key.startswith("bond-"), oc.items())
149 overrides = map(lambda (key,val): (key[5:], val), overrides)
150 bond_options.update(overrides)
151 for (name,val) in bond_options.items():
152 # XXX need ovs-vsctl support for bond options
153 #argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
157 def datapath_deconfigure_bond(netdev):
158 return ['--', '--if-exists', 'del-port', netdev]
160 def datapath_deconfigure_ipdev(interface):
161 return ['--', '--if-exists', 'del-port', interface]
163 def datapath_modify_config(commands):
164 #log("modifying configuration:")
168 rc = run_command(['/usr/bin/ovs-vsctl'] + ['--timeout=20']
169 + [c for c in commands if not c.startswith('#')])
171 raise Error("Failed to modify vswitch configuration")
175 # Toplevel Datapath Configuration.
178 def configure_datapath(pif, parent=None, vlan=None):
179 """Bring up the datapath configuration for PIF.
181 Should be careful not to glitch existing users of the datapath, e.g. other VLANs etc.
183 Should take care of tearing down other PIFs which encompass common physical devices.
185 Returns a tuple containing
186 - A list containing the necessary vsctl command line arguments
187 - A list of additional devices which should be brought up after
188 the configuration is applied.
194 bridge = pif_bridge_name(pif)
196 physical_devices = datapath_get_physical_pifs(pif)
198 # Determine additional devices to deconfigure.
200 # Given all physical devices which are part of this PIF we need to
202 # - any additional bond which a physical device is part of.
203 # - any additional physical devices which are part of an additional bond.
205 # Any of these which are not currently in use should be brought
206 # down and deconfigured.
207 extra_down_bonds = []
208 extra_down_ports = []
209 for p in physical_devices:
210 for bond in pif_get_bond_masters(p):
212 log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond))
214 if bond in extra_down_bonds:
216 if db().get_pif_record(bond)['currently_attached']:
217 log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond))
219 extra_down_bonds += [bond]
221 for s in pif_get_bond_slaves(bond):
222 if s in physical_devices:
224 if s in extra_down_ports:
226 if pif_currently_in_use(s):
228 extra_down_ports += [s]
230 log("configure_datapath: bridge - %s" % bridge)
231 log("configure_datapath: physical - %s" % [pif_netdev_name(p) for p in physical_devices])
232 log("configure_datapath: extra ports - %s" % [pif_netdev_name(p) for p in extra_down_ports])
233 log("configure_datapath: extra bonds - %s" % [pif_netdev_name(p) for p in extra_down_bonds])
235 # Need to fully deconfigure any bridge which any of the:
240 for brpif in physical_devices + extra_down_ports + extra_down_bonds:
243 b = pif_bridge_name(brpif)
247 vsctl_argv += ['# remove bridge %s' % b]
248 vsctl_argv += ['--', '--if-exists', 'del-br', b]
250 for n in extra_down_ports:
251 dev = pif_netdev_name(n)
252 vsctl_argv += ['# deconfigure sibling physical device %s' % dev]
253 vsctl_argv += datapath_deconfigure_physical(dev)
256 for n in extra_down_bonds:
257 dev = pif_netdev_name(n)
258 vsctl_argv += ['# deconfigure bond device %s' % dev]
259 vsctl_argv += datapath_deconfigure_bond(dev)
262 for p in physical_devices:
263 dev = pif_netdev_name(p)
264 vsctl_argv += ['# deconfigure physical port %s' % dev]
265 vsctl_argv += datapath_deconfigure_physical(dev)
267 if parent and datapath:
268 vsctl_argv += ['--', '--may-exist', 'add-br', bridge, parent, vlan]
270 vsctl_argv += ['--', '--may-exist', 'add-br', bridge]
272 if len(physical_devices) > 1:
273 vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
274 vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
275 vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)]
276 vsctl_argv += datapath_configure_bond(pif, physical_devices)
277 extra_up_ports += [pif_netdev_name(pif)]
279 iface = pif_netdev_name(physical_devices[0])
280 vsctl_argv += ['# add physical device %s' % iface]
281 vsctl_argv += ['--', '--may-exist', 'add-port', bridge, iface]
283 return vsctl_argv,extra_up_ports
285 def deconfigure_datapath(pif):
288 bridge = pif_bridge_name(pif)
290 physical_devices = datapath_get_physical_pifs(pif)
292 log("deconfigure_datapath: bridge - %s" % bridge)
293 log("deconfigure_datapath: physical devices - %s" % [pif_netdev_name(p) for p in physical_devices])
295 for p in physical_devices:
296 dev = pif_netdev_name(p)
297 vsctl_argv += ['# deconfigure physical port %s' % dev]
298 vsctl_argv += datapath_deconfigure_physical(dev)
301 if len(physical_devices) > 1:
302 vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
303 vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
305 vsctl_argv += ['# deconfigure bridge %s' % bridge]
306 vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
314 class DatapathVswitch(Datapath):
315 def __init__(self, pif):
316 Datapath.__init__(self, pif)
317 self._dp = pif_datapath(pif)
318 self._ipdev = pif_ipdev_name(pif)
320 if pif_is_vlan(pif) and not self._dp:
321 raise Error("Unbridged VLAN devices not implemented yet")
323 log("Configured for Vswitch datapath")
325 def configure_ipdev(self, cfg):
326 cfg.write("TYPE=Ethernet\n")
328 def preconfigure(self, parent):
332 pifrec = db().get_pif_record(self._pif)
333 dprec = db().get_pif_record(self._dp)
336 bridge = pif_bridge_name(self._dp)
337 if pif_is_vlan(self._pif):
338 datapath = pif_datapath(self._pif)
339 c,e = configure_datapath(self._dp, datapath, pifrec['VLAN'])
341 c,e = configure_datapath(self._dp)
345 xs_network_uuids = []
346 for nwpif in db().get_pifs_by_device(db().get_pif_record(self._pif)['device']):
347 rec = db().get_pif_record(nwpif)
349 # When state is read from dbcache PIF.currently_attached
350 # is always assumed to be false... Err on the side of
351 # listing even detached networks for the time being.
352 #if nwpif != pif and not rec['currently_attached']:
353 # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
355 nwrec = db().get_network_record(rec['network'])
356 xs_network_uuids += [nwrec['uuid']]
358 vsctl_argv += ['# configure xs-network-uuids']
359 vsctl_argv += ['--', 'br-set-external-id', bridge,
360 'xs-network-uuids', ';'.join(xs_network_uuids)]
363 vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
364 vsctl_argv += datapath_deconfigure_ipdev(ipdev)
365 vsctl_argv += ["# reconfigure ipdev %s" % ipdev]
366 vsctl_argv += ['--', 'add-port', bridge, ipdev]
368 # XXX Needs support in ovs-vsctl
370 # vsctl_argv += ['--add=bridge.%s.mac=%s' % (bridge, dprec['MAC'])]
372 # vsctl_argv += ['--add=iface.%s.mac=%s' % (ipdev, dprec['MAC'])]
374 self._vsctl_argv = vsctl_argv
375 self._extra_ports = extra_ports
377 def bring_down_existing(self):
381 # Bring up physical devices. ovs-vswitchd initially enables or
382 # disables bond slaves based on whether carrier is detected
383 # when they are added, and a network device that is down
384 # always reports "no carrier".
385 physical_devices = datapath_get_physical_pifs(self._dp)
387 for p in physical_devices:
388 oc = db().get_pif_record(p)['other_config']
390 dev = pif_netdev_name(p)
392 mtu = mtu_setting(oc)
396 settings, offload = ethtool_settings(oc)
398 run_command(['/sbin/ethtool', '-s', dev] + settings)
400 run_command(['/sbin/ethtool', '-K', dev] + offload)
402 datapath_modify_config(self._vsctl_argv)
405 for p in self._extra_ports:
406 log("action_up: bring up %s" % p)
409 def bring_down(self):
415 bridge = pif_bridge_name(dp)
417 #nw = db().get_pif_record(self._pif)['network']
418 #nwrec = db().get_network_record(nw)
419 #vsctl_argv += ['# deconfigure xs-network-uuids']
420 #vsctl_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])]
422 log("deconfigure ipdev %s on %s" % (ipdev,bridge))
423 vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
424 vsctl_argv += datapath_deconfigure_ipdev(ipdev)
426 if pif_is_vlan(self._pif):
427 # If the VLAN's slave is attached, leave datapath setup.
428 slave = pif_get_vlan_slave(self._pif)
429 if db().get_pif_record(slave)['currently_attached']:
430 log("action_down: vlan slave is currently attached")
433 # If the VLAN's slave has other VLANs that are attached, leave datapath setup.
434 for master in pif_get_vlan_masters(slave):
435 if master != self._pif and db().get_pif_record(master)['currently_attached']:
436 log("action_down: vlan slave has other master: %s" % pif_netdev_name(master))
439 # Otherwise, take down the datapath too (fall through)
441 log("action_down: no more masters, bring down slave %s" % bridge)
443 # Stop here if this PIF has attached VLAN masters.
444 masters = [db().get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(self._pif) if db().get_pif_record(m)['currently_attached']]
446 log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters))
450 vsctl_argv += deconfigure_datapath(dp)
451 datapath_modify_config(vsctl_argv)