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_currently_in_use(pif):
44 """Determine if a PIF is currently in use.
46 A PIF is determined to be currently in use if
47 - PIF.currently-attached is true
48 - Any bond master is currently attached
49 - Any VLAN master is currently attached
51 rec = db().get_pif_record(pif)
52 if rec['currently_attached']:
53 log("configure_datapath: %s is currently attached" % (pif_netdev_name(pif)))
55 for b in pif_get_bond_masters(pif):
56 if pif_currently_in_use(b):
57 log("configure_datapath: %s is in use by BOND master %s" % (pif_netdev_name(pif),pif_netdev_name(b)))
59 for v in pif_get_vlan_masters(pif):
60 if pif_currently_in_use(v):
61 log("configure_datapath: %s is in use by VLAN master %s" % (pif_netdev_name(pif),pif_netdev_name(v)))
66 # Datapath Configuration
69 def pif_datapath(pif):
70 """Return the datapath PIF associated with PIF.
71 A non-VLAN PIF is its own datapath PIF, except that a bridgeless PIF has
72 no datapath PIF at all.
73 A VLAN PIF's datapath PIF is its VLAN slave's datapath PIF.
76 return pif_datapath(pif_get_vlan_slave(pif))
78 pifrec = db().get_pif_record(pif)
79 nwrec = db().get_network_record(pifrec['network'])
80 if not nwrec['bridge']:
85 def datapath_get_physical_pifs(pif):
86 """Return the PIFs for the physical network device(s) associated with a datapath PIF.
87 For a bond master PIF, these are the bond slave PIFs.
88 For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
90 A VLAN PIF cannot be a datapath PIF.
93 # Seems like overkill...
94 raise Error("get-physical-pifs should not get passed a VLAN")
95 elif pif_is_bond(pif):
96 return pif_get_bond_slaves(pif)
100 def datapath_deconfigure_physical(netdev):
101 return ['--', '--with-iface', '--if-exists', 'del-port', netdev]
103 def datapath_configure_bond(pif,slaves):
104 bridge = pif_bridge_name(pif)
105 pifrec = db().get_pif_record(pif)
106 interface = pif_netdev_name(pif)
108 argv = ['--', '--fake-iface', 'add-bond', bridge, interface]
110 argv += [pif_netdev_name(slave)]
112 # XXX need ovs-vsctl support
113 #if pifrec['MAC'] != "":
114 # argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
118 "mode": "balance-slb",
124 # override defaults with values from other-config whose keys
126 oc = pifrec['other_config']
127 overrides = filter(lambda (key,val):
128 key.startswith("bond-"), oc.items())
129 overrides = map(lambda (key,val): (key[5:], val), overrides)
130 bond_options.update(overrides)
131 for (name,val) in bond_options.items():
132 # XXX need ovs-vsctl support for bond options
133 #argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
137 def datapath_deconfigure_bond(netdev):
138 return ['--', '--with-iface', '--if-exists', 'del-port', netdev]
140 def datapath_deconfigure_ipdev(interface):
141 return ['--', '--with-iface', '--if-exists', 'del-port', interface]
143 def datapath_modify_config(commands):
144 #log("modifying configuration:")
148 rc = run_command(['/usr/bin/ovs-vsctl'] + ['--timeout=20']
149 + [c for c in commands if not c.startswith('#')])
151 raise Error("Failed to modify vswitch configuration")
155 # Toplevel Datapath Configuration.
158 def configure_datapath(pif, parent=None, vlan=None):
159 """Bring up the datapath configuration for PIF.
161 Should be careful not to glitch existing users of the datapath, e.g. other VLANs etc.
163 Should take care of tearing down other PIFs which encompass common physical devices.
165 Returns a tuple containing
166 - A list containing the necessary vsctl command line arguments
167 - A list of additional devices which should be brought up after
168 the configuration is applied.
174 bridge = pif_bridge_name(pif)
176 physical_devices = datapath_get_physical_pifs(pif)
178 # Determine additional devices to deconfigure.
180 # Given all physical devices which are part of this PIF we need to
182 # - any additional bond which a physical device is part of.
183 # - any additional physical devices which are part of an additional bond.
185 # Any of these which are not currently in use should be brought
186 # down and deconfigured.
187 extra_down_bonds = []
188 extra_down_ports = []
189 for p in physical_devices:
190 for bond in pif_get_bond_masters(p):
192 log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond))
194 if bond in extra_down_bonds:
196 if db().get_pif_record(bond)['currently_attached']:
197 log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond))
199 extra_down_bonds += [bond]
201 for s in pif_get_bond_slaves(bond):
202 if s in physical_devices:
204 if s in extra_down_ports:
206 if pif_currently_in_use(s):
208 extra_down_ports += [s]
210 log("configure_datapath: bridge - %s" % bridge)
211 log("configure_datapath: physical - %s" % [pif_netdev_name(p) for p in physical_devices])
212 log("configure_datapath: extra ports - %s" % [pif_netdev_name(p) for p in extra_down_ports])
213 log("configure_datapath: extra bonds - %s" % [pif_netdev_name(p) for p in extra_down_bonds])
215 # Need to fully deconfigure any bridge which any of the:
220 for brpif in physical_devices + extra_down_ports + extra_down_bonds:
223 b = pif_bridge_name(brpif)
227 vsctl_argv += ['# remove bridge %s' % b]
228 vsctl_argv += ['--', '--if-exists', 'del-br', b]
230 for n in extra_down_ports:
231 dev = pif_netdev_name(n)
232 vsctl_argv += ['# deconfigure sibling physical device %s' % dev]
233 vsctl_argv += datapath_deconfigure_physical(dev)
236 for n in extra_down_bonds:
237 dev = pif_netdev_name(n)
238 vsctl_argv += ['# deconfigure bond device %s' % dev]
239 vsctl_argv += datapath_deconfigure_bond(dev)
242 for p in physical_devices:
243 dev = pif_netdev_name(p)
244 vsctl_argv += ['# deconfigure physical port %s' % dev]
245 vsctl_argv += datapath_deconfigure_physical(dev)
247 if parent and datapath:
248 vsctl_argv += ['--', '--may-exist', 'add-br', bridge, parent, vlan]
250 vsctl_argv += ['--', '--may-exist', 'add-br', bridge]
252 if len(physical_devices) > 1:
253 vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
254 vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
255 vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)]
256 vsctl_argv += datapath_configure_bond(pif, physical_devices)
257 extra_up_ports += [pif_netdev_name(pif)]
259 iface = pif_netdev_name(physical_devices[0])
260 vsctl_argv += ['# add physical device %s' % iface]
261 vsctl_argv += ['--', '--may-exist', 'add-port', bridge, iface]
263 return vsctl_argv,extra_up_ports
265 def deconfigure_datapath(pif):
268 bridge = pif_bridge_name(pif)
270 physical_devices = datapath_get_physical_pifs(pif)
272 log("deconfigure_datapath: bridge - %s" % bridge)
273 log("deconfigure_datapath: physical devices - %s" % [pif_netdev_name(p) for p in physical_devices])
275 for p in physical_devices:
276 dev = pif_netdev_name(p)
277 vsctl_argv += ['# deconfigure physical port %s' % dev]
278 vsctl_argv += datapath_deconfigure_physical(dev)
281 if len(physical_devices) > 1:
282 vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
283 vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
285 vsctl_argv += ['# deconfigure bridge %s' % bridge]
286 vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
294 class DatapathVswitch(Datapath):
295 def __init__(self, pif):
296 Datapath.__init__(self, pif)
297 self._dp = pif_datapath(pif)
298 self._ipdev = pif_ipdev_name(pif)
300 if pif_is_vlan(pif) and not self._dp:
301 raise Error("Unbridged VLAN devices not implemented yet")
303 log("Configured for Vswitch datapath")
305 def configure_ipdev(self, cfg):
306 cfg.write("TYPE=Ethernet\n")
308 def preconfigure(self, parent):
312 pifrec = db().get_pif_record(self._pif)
313 dprec = db().get_pif_record(self._dp)
316 bridge = pif_bridge_name(self._dp)
317 if pif_is_vlan(self._pif):
318 datapath = pif_datapath(self._pif)
319 c,e = configure_datapath(self._dp, datapath, pifrec['VLAN'])
321 c,e = configure_datapath(self._dp)
325 xs_network_uuids = []
326 for nwpif in db().get_pifs_by_device(db().get_pif_record(self._pif)['device']):
327 rec = db().get_pif_record(nwpif)
329 # When state is read from dbcache PIF.currently_attached
330 # is always assumed to be false... Err on the side of
331 # listing even detached networks for the time being.
332 #if nwpif != pif and not rec['currently_attached']:
333 # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
335 nwrec = db().get_network_record(rec['network'])
336 xs_network_uuids += [nwrec['uuid']]
338 vsctl_argv += ['# configure xs-network-uuids']
339 vsctl_argv += ['--', 'br-set-external-id', bridge,
340 'xs-network-uuids', ';'.join(xs_network_uuids)]
343 vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
344 vsctl_argv += datapath_deconfigure_ipdev(ipdev)
345 vsctl_argv += ["# reconfigure ipdev %s" % ipdev]
346 vsctl_argv += ['--', 'add-port', bridge, ipdev]
348 # XXX Needs support in ovs-vsctl
350 # vsctl_argv += ['--add=bridge.%s.mac=%s' % (bridge, dprec['MAC'])]
352 # vsctl_argv += ['--add=iface.%s.mac=%s' % (ipdev, dprec['MAC'])]
354 self._vsctl_argv = vsctl_argv
355 self._extra_ports = extra_ports
357 def bring_down_existing(self):
361 # Bring up physical devices. ovs-vswitchd initially enables or
362 # disables bond slaves based on whether carrier is detected
363 # when they are added, and a network device that is down
364 # always reports "no carrier".
365 physical_devices = datapath_get_physical_pifs(self._dp)
367 for p in physical_devices:
368 oc = db().get_pif_record(p)['other_config']
370 dev = pif_netdev_name(p)
372 mtu = mtu_setting(oc)
376 settings, offload = ethtool_settings(oc)
378 run_command(['/sbin/ethtool', '-s', dev] + settings)
380 run_command(['/sbin/ethtool', '-K', dev] + offload)
382 datapath_modify_config(self._vsctl_argv)
385 for p in self._extra_ports:
386 log("action_up: bring up %s" % p)
389 def bring_down(self):
395 bridge = pif_bridge_name(dp)
397 #nw = db().get_pif_record(self._pif)['network']
398 #nwrec = db().get_network_record(nw)
399 #vsctl_argv += ['# deconfigure xs-network-uuids']
400 #vsctl_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])]
402 log("deconfigure ipdev %s on %s" % (ipdev,bridge))
403 vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
404 vsctl_argv += datapath_deconfigure_ipdev(ipdev)
406 if pif_is_vlan(self._pif):
407 # If the VLAN's slave is attached, leave datapath setup.
408 slave = pif_get_vlan_slave(self._pif)
409 if db().get_pif_record(slave)['currently_attached']:
410 log("action_down: vlan slave is currently attached")
413 # If the VLAN's slave has other VLANs that are attached, leave datapath setup.
414 for master in pif_get_vlan_masters(slave):
415 if master != self._pif and db().get_pif_record(master)['currently_attached']:
416 log("action_down: vlan slave has other master: %s" % pif_netdev_name(master))
419 # Otherwise, take down the datapath too (fall through)
421 log("action_down: no more masters, bring down slave %s" % bridge)
423 # Stop here if this PIF has attached VLAN masters.
424 masters = [db().get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(self._pif) if db().get_pif_record(m)['currently_attached']]
426 log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters))
430 vsctl_argv += deconfigure_datapath(dp)
431 datapath_modify_config(vsctl_argv)