1 # Copyright (c) 2008,2009 Citrix Systems, Inc.
2 # Copyright (c) 2009 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):
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):
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 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 += ['--', '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)
331 bridge = pif_bridge_name(self._dp)
332 c,e = configure_datapath(self._dp)
337 datapath = pif_datapath(pif)
338 vsctl_argv += ['--', 'add-br', bridge, datapath, pifrec['VLAN']]
340 vsctl_argv += ['--', 'add-br', bridge]
342 xs_network_uuids = []
343 for nwpif in db().get_pifs_by_device(db().get_pif_record(self._pif)['device']):
344 rec = db().get_pif_record(nwpif)
346 # When state is read from dbcache PIF.currently_attached
347 # is always assumed to be false... Err on the side of
348 # listing even detached networks for the time being.
349 #if nwpif != pif and not rec['currently_attached']:
350 # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
352 nwrec = db().get_network_record(rec['network'])
353 xs_network_uuids += [nwrec['uuid']]
355 vsctl_argv += ['# configure xs-network-uuids']
356 vsctl_argv += ['--', 'br-set-external-id', bridge,
357 '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, pifrec['MAC'])]
368 # vsctl_argv += ['--add=iface.%s.mac=%s' % (ipdev, pifrec['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)