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):
159 """Bring up the configuration for 'pif', which must not be a VLAN PIF, by:
160 - Tearing down other PIFs that use the same physical devices as 'pif'.
161 - Ensuring that 'pif' itself is set up.
162 - *Not* tearing down any PIFs that are stacked on top of 'pif' (i.e. VLANs
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 vsctl_argv += ['--', '--may-exist', 'add-br', bridge]
249 if len(physical_devices) > 1:
250 vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
251 vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
252 vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)]
253 vsctl_argv += datapath_configure_bond(pif, physical_devices)
254 extra_up_ports += [pif_netdev_name(pif)]
256 iface = pif_netdev_name(physical_devices[0])
257 vsctl_argv += ['# add physical device %s' % iface]
258 vsctl_argv += ['--', '--may-exist', 'add-port', bridge, iface]
260 return vsctl_argv,extra_up_ports
262 def deconfigure_datapath(pif):
265 bridge = pif_bridge_name(pif)
267 physical_devices = datapath_get_physical_pifs(pif)
269 log("deconfigure_datapath: bridge - %s" % bridge)
270 log("deconfigure_datapath: physical devices - %s" % [pif_netdev_name(p) for p in physical_devices])
272 for p in physical_devices:
273 dev = pif_netdev_name(p)
274 vsctl_argv += ['# deconfigure physical port %s' % dev]
275 vsctl_argv += datapath_deconfigure_physical(dev)
278 if len(physical_devices) > 1:
279 vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
280 vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
282 vsctl_argv += ['# deconfigure bridge %s' % bridge]
283 vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
291 class DatapathVswitch(Datapath):
292 def __init__(self, pif):
293 Datapath.__init__(self, pif)
294 self._dp = pif_datapath(pif)
295 self._ipdev = pif_ipdev_name(pif)
297 if pif_is_vlan(pif) and not self._dp:
298 raise Error("Unbridged VLAN devices not implemented yet")
300 log("Configured for Vswitch datapath")
302 def configure_ipdev(self, cfg):
303 cfg.write("TYPE=Ethernet\n")
305 def preconfigure(self, parent):
309 pifrec = db().get_pif_record(self._pif)
310 dprec = db().get_pif_record(self._dp)
313 c,e = configure_datapath(self._dp)
314 bridge = pif_bridge_name(self._pif)
318 if pif_is_vlan(self._pif):
319 # XXX this is only needed on XS5.5, because XAPI misguidedly
320 # creates the fake bridge (via bridge ioctl) before it calls us.
321 vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
323 # configure_datapath() set up the underlying datapath bridge.
324 # Stack a VLAN bridge on top of it.
325 vsctl_argv += ['--', '--may-exist', 'add-br',
326 bridge, pif_bridge_name(self._dp), pifrec['VLAN']]
327 xs_network_uuids = []
328 for nwpif in db().get_pifs_by_device(db().get_pif_record(self._pif)['device']):
329 rec = db().get_pif_record(nwpif)
331 # When state is read from dbcache PIF.currently_attached
332 # is always assumed to be false... Err on the side of
333 # listing even detached networks for the time being.
334 #if nwpif != pif and not rec['currently_attached']:
335 # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
337 nwrec = db().get_network_record(rec['network'])
338 xs_network_uuids += [nwrec['uuid']]
340 vsctl_argv += ['# configure xs-network-uuids']
341 vsctl_argv += ['--', 'br-set-external-id', bridge,
342 'xs-network-uuids', ';'.join(xs_network_uuids)]
345 vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
346 vsctl_argv += datapath_deconfigure_ipdev(ipdev)
347 vsctl_argv += ["# reconfigure ipdev %s" % ipdev]
348 vsctl_argv += ['--', 'add-port', bridge, ipdev]
350 # XXX Needs support in ovs-vsctl
352 # vsctl_argv += ['--add=bridge.%s.mac=%s' % (bridge, dprec['MAC'])]
354 # vsctl_argv += ['--add=iface.%s.mac=%s' % (ipdev, dprec['MAC'])]
356 self._vsctl_argv = vsctl_argv
357 self._extra_ports = extra_ports
359 def bring_down_existing(self):
363 # Bring up physical devices. ovs-vswitchd initially enables or
364 # disables bond slaves based on whether carrier is detected
365 # when they are added, and a network device that is down
366 # always reports "no carrier".
367 physical_devices = datapath_get_physical_pifs(self._dp)
369 for p in physical_devices:
370 oc = db().get_pif_record(p)['other_config']
372 dev = pif_netdev_name(p)
374 mtu = mtu_setting(oc)
378 settings, offload = ethtool_settings(oc)
380 run_command(['/sbin/ethtool', '-s', dev] + settings)
382 run_command(['/sbin/ethtool', '-K', dev] + offload)
384 datapath_modify_config(self._vsctl_argv)
387 for p in self._extra_ports:
388 log("action_up: bring up %s" % p)
391 def bring_down(self):
397 bridge = pif_bridge_name(dp)
399 #nw = db().get_pif_record(self._pif)['network']
400 #nwrec = db().get_network_record(nw)
401 #vsctl_argv += ['# deconfigure xs-network-uuids']
402 #vsctl_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])]
404 log("deconfigure ipdev %s on %s" % (ipdev,bridge))
405 vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
406 vsctl_argv += datapath_deconfigure_ipdev(ipdev)
408 if pif_is_vlan(self._pif):
409 # If the VLAN's slave is attached, leave datapath setup.
410 slave = pif_get_vlan_slave(self._pif)
411 if db().get_pif_record(slave)['currently_attached']:
412 log("action_down: vlan slave is currently attached")
415 # If the VLAN's slave has other VLANs that are attached, leave datapath setup.
416 for master in pif_get_vlan_masters(slave):
417 if master != self._pif and db().get_pif_record(master)['currently_attached']:
418 log("action_down: vlan slave has other master: %s" % pif_netdev_name(master))
421 # Otherwise, take down the datapath too (fall through)
423 log("action_down: no more masters, bring down slave %s" % bridge)
425 # Stop here if this PIF has attached VLAN masters.
426 masters = [db().get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(self._pif) if db().get_pif_record(m)['currently_attached']]
428 log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters))
432 vsctl_argv += deconfigure_datapath(dp)
433 datapath_modify_config(vsctl_argv)