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 *
19 # Bare Network Devices -- network devices without IP configuration
22 def netdev_down(netdev):
23 """Bring down a bare network device"""
24 if not netdev_exists(netdev):
25 log("netdev: down: device %s does not exist, ignoring" % netdev)
27 run_command(["/sbin/ifconfig", netdev, 'down'])
29 def netdev_up(netdev, mtu=None):
30 """Bring up a bare network device"""
31 if not netdev_exists(netdev):
32 raise Error("netdev: up: device %s does not exist" % netdev)
39 run_command(["/sbin/ifconfig", netdev, 'up'] + mtu)
45 def pif_currently_in_use(pif):
46 """Determine if a PIF is currently in use.
48 A PIF is determined to be currently in use if
49 - PIF.currently-attached is true
50 - Any bond master is currently attached
51 - Any VLAN master is currently attached
53 rec = db().get_pif_record(pif)
54 if rec['currently_attached']:
55 log("configure_datapath: %s is currently attached" % (pif_netdev_name(pif)))
57 for b in pif_get_bond_masters(pif):
58 if pif_currently_in_use(b):
59 log("configure_datapath: %s is in use by BOND master %s" % (pif_netdev_name(pif),pif_netdev_name(b)))
61 for v in pif_get_vlan_masters(pif):
62 if pif_currently_in_use(v):
63 log("configure_datapath: %s is in use by VLAN master %s" % (pif_netdev_name(pif),pif_netdev_name(v)))
68 # Datapath Configuration
71 def pif_datapath(pif):
72 """Return the datapath PIF associated with PIF.
73 A non-VLAN PIF is its own datapath PIF, except that a bridgeless PIF has
74 no datapath PIF at all.
75 A VLAN PIF's datapath PIF is its VLAN slave's datapath PIF.
78 return pif_datapath(pif_get_vlan_slave(pif))
80 pifrec = db().get_pif_record(pif)
81 nwrec = db().get_network_record(pifrec['network'])
82 if not nwrec['bridge']:
87 def datapath_get_physical_pifs(pif):
88 """Return the PIFs for the physical network device(s) associated with a datapath PIF.
89 For a bond master PIF, these are the bond slave PIFs.
90 For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
92 A VLAN PIF cannot be a datapath PIF.
94 if pif_is_tunnel(pif):
96 elif pif_is_vlan(pif):
97 # Seems like overkill...
98 raise Error("get-physical-pifs should not get passed a VLAN")
99 elif pif_is_bond(pif):
100 return pif_get_bond_slaves(pif)
104 def datapath_deconfigure_physical(netdev):
105 return ['--', '--with-iface', '--if-exists', 'del-port', netdev]
114 raise Error("strings may not contain null bytes")
128 return r'\x%02x' % ord(c)
129 return '"' + re.sub(r'["\\\000-\037]', escape, s) + '"'
131 def datapath_configure_tunnel(pif):
134 def datapath_configure_bond(pif,slaves):
135 bridge = pif_bridge_name(pif)
136 pifrec = db().get_pif_record(pif)
137 interface = pif_netdev_name(pif)
139 argv = ['--', '--fake-iface', 'add-bond', bridge, interface]
141 argv += [pif_netdev_name(slave)]
145 "mode": "balance-slb",
151 # override defaults with values from other-config whose keys
153 oc = pifrec['other_config']
154 overrides = filter(lambda (key,val):
155 key.startswith("bond-"), oc.items())
156 overrides = map(lambda (key,val): (key[5:], val), overrides)
157 bond_options.update(overrides)
159 argv += ['--', 'set', 'Port', interface]
160 if pifrec['MAC'] != "":
161 argv += ['MAC=%s' % vsctl_escape(pifrec['MAC'])]
162 for (name,val) in bond_options.items():
163 if name in ['updelay', 'downdelay']:
164 # updelay and downdelay have dedicated schema columns.
165 # The value must be a nonnegative integer.
171 argv += ['bond_%s=%d' % (name, value)]
173 log("bridge %s has invalid %s '%s'" % (bridge, name, value))
174 elif name in ['miimon', 'use_carrier']:
180 if name == 'use_carrier':
185 argv += ["other-config:bond-detect-mode=%s" % value]
187 argv += ["other-config:bond-miimon-interval=%d" % value]
189 log("bridge %s has invalid %s '%s'" % (bridge, name, value))
192 if val in ['balance-slb', 'active-backup']:
193 argv += ['bond_%s=%s' % (name, val)]
195 log("bridge %s has invalid %s '%s'" % (bridge, name, val))
197 # Pass other bond options into other_config.
198 argv += ["other-config:%s=%s" % (vsctl_escape("bond-%s" % name),
202 def datapath_deconfigure_bond(netdev):
203 return ['--', '--with-iface', '--if-exists', 'del-port', netdev]
205 def datapath_deconfigure_ipdev(interface):
206 return ['--', '--with-iface', '--if-exists', 'del-port', interface]
208 def datapath_modify_config(commands):
209 #log("modifying configuration:")
213 rc = run_command(['/usr/bin/ovs-vsctl'] + ['--timeout=20']
214 + [c for c in commands if not c.startswith('#')])
216 raise Error("Failed to modify vswitch configuration")
220 # Toplevel Datapath Configuration.
223 def configure_datapath(pif):
224 """Bring up the configuration for 'pif', which must not be a VLAN PIF, by:
225 - Tearing down other PIFs that use the same physical devices as 'pif'.
226 - Ensuring that 'pif' itself is set up.
227 - *Not* tearing down any PIFs that are stacked on top of 'pif' (i.e. VLANs
230 Returns a tuple containing
231 - A list containing the necessary vsctl command line arguments
232 - A list of additional devices which should be brought up after
233 the configuration is applied.
239 assert not pif_is_vlan(pif)
240 bridge = pif_bridge_name(pif)
242 physical_devices = datapath_get_physical_pifs(pif)
244 vsctl_argv += ['## configuring datapath %s' % bridge]
246 # Determine additional devices to deconfigure.
248 # Given all physical devices which are part of this PIF we need to
250 # - any additional bond which a physical device is part of.
251 # - any additional physical devices which are part of an additional bond.
253 # Any of these which are not currently in use should be brought
254 # down and deconfigured.
255 extra_down_bonds = []
256 extra_down_ports = []
257 for p in physical_devices:
258 for bond in pif_get_bond_masters(p):
260 log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond))
262 if bond in extra_down_bonds:
264 if db().get_pif_record(bond)['currently_attached']:
265 log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond))
267 extra_down_bonds += [bond]
269 for s in pif_get_bond_slaves(bond):
270 if s in physical_devices:
272 if s in extra_down_ports:
274 if pif_currently_in_use(s):
276 extra_down_ports += [s]
278 log("configure_datapath: bridge - %s" % bridge)
279 log("configure_datapath: physical - %s" % [pif_netdev_name(p) for p in physical_devices])
280 log("configure_datapath: extra ports - %s" % [pif_netdev_name(p) for p in extra_down_ports])
281 log("configure_datapath: extra bonds - %s" % [pif_netdev_name(p) for p in extra_down_bonds])
283 # Need to fully deconfigure any bridge which any of the:
288 for brpif in physical_devices + extra_down_ports + extra_down_bonds:
291 b = pif_bridge_name(brpif)
295 vsctl_argv += ['# remove bridge %s' % b]
296 vsctl_argv += ['--', '--if-exists', 'del-br', b]
298 for n in extra_down_ports:
299 dev = pif_netdev_name(n)
300 vsctl_argv += ['# deconfigure sibling physical device %s' % dev]
301 vsctl_argv += datapath_deconfigure_physical(dev)
304 for n in extra_down_bonds:
305 dev = pif_netdev_name(n)
306 vsctl_argv += ['# deconfigure bond device %s' % dev]
307 vsctl_argv += datapath_deconfigure_bond(dev)
310 for p in physical_devices:
311 dev = pif_netdev_name(p)
312 vsctl_argv += ['# deconfigure physical port %s' % dev]
313 vsctl_argv += datapath_deconfigure_physical(dev)
315 vsctl_argv += ['--', '--may-exist', 'add-br', bridge]
317 if len(physical_devices) > 1:
318 vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
319 vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
320 vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)]
321 vsctl_argv += datapath_configure_bond(pif, physical_devices)
322 extra_up_ports += [pif_netdev_name(pif)]
323 elif len(physical_devices) == 1:
324 iface = pif_netdev_name(physical_devices[0])
325 vsctl_argv += ['# add physical device %s' % iface]
326 vsctl_argv += ['--', '--may-exist', 'add-port', bridge, iface]
327 elif pif_is_tunnel(pif):
328 datapath_configure_tunnel(pif)
330 vsctl_argv += ['# configure Bridge MAC']
331 vsctl_argv += ['--', 'set', 'Bridge', bridge,
332 'other-config:hwaddr=%s' % vsctl_escape(db().get_pif_record(pif)['MAC'])]
334 pool = db().get_pool_record()
335 network = db().get_network_by_bridge(bridge)
337 valid_fail_modes = ['standalone', 'secure']
340 network_rec = db().get_network_record(network)
341 fail_mode = network_rec['other_config'].get('vswitch-controller-fail-mode')
343 if (fail_mode not in valid_fail_modes) and pool:
344 fail_mode = pool['other_config'].get('vswitch-controller-fail-mode')
346 if fail_mode not in valid_fail_modes:
347 fail_mode = 'standalone'
349 vsctl_argv += ['--', 'set', 'Bridge', bridge, 'fail_mode=%s' % fail_mode]
351 vsctl_argv += set_br_external_ids(pif)
352 vsctl_argv += ['## done configuring datapath %s' % bridge]
354 return vsctl_argv,extra_up_ports
356 def deconfigure_bridge(pif):
359 bridge = pif_bridge_name(pif)
361 log("deconfigure_bridge: bridge - %s" % bridge)
363 vsctl_argv += ['# deconfigure bridge %s' % bridge]
364 vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
368 def set_br_external_ids(pif):
369 pifrec = db().get_pif_record(pif)
370 dp = pif_datapath(pif)
371 dprec = db().get_pif_record(dp)
373 xs_network_uuids = []
374 for nwpif in db().get_pifs_by_device(pifrec['device']):
375 rec = db().get_pif_record(nwpif)
377 # When state is read from dbcache PIF.currently_attached
378 # is always assumed to be false... Err on the side of
379 # listing even detached networks for the time being.
380 #if nwpif != pif and not rec['currently_attached']:
381 # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
383 nwrec = db().get_network_record(rec['network'])
386 if pif_is_vlan(nwpif):
387 xs_network_uuids.append(uuid)
389 xs_network_uuids.insert(0, uuid)
392 vsctl_argv += ['# configure xs-network-uuids']
393 vsctl_argv += ['--', 'br-set-external-id', pif_bridge_name(pif),
394 'xs-network-uuids', ';'.join(xs_network_uuids)]
402 class DatapathVswitch(Datapath):
403 def __init__(self, pif):
404 Datapath.__init__(self, pif)
405 self._dp = pif_datapath(pif)
406 self._ipdev = pif_ipdev_name(pif)
408 if pif_is_vlan(pif) and not self._dp:
409 raise Error("Unbridged VLAN devices not implemented yet")
411 log("Configured for Vswitch datapath")
415 if not os.path.exists("/var/run/openvswitch/db.sock"):
416 # ovsdb-server is not running, so we can't update the database.
417 # Probably we are being called as part of system shutdown. Just
418 # skip the update, since the external-ids will be updated on the
423 for pif in db().get_all_pifs():
424 pifrec = db().get_pif_record(pif)
425 if not pif_is_vlan(pif) and pifrec['currently_attached']:
426 vsctl_argv += set_br_external_ids(pif)
429 datapath_modify_config(vsctl_argv)
431 def configure_ipdev(self, cfg):
432 cfg.write("TYPE=Ethernet\n")
434 def preconfigure(self, parent):
438 pifrec = db().get_pif_record(self._pif)
439 dprec = db().get_pif_record(self._dp)
442 c,e = configure_datapath(self._dp)
443 bridge = pif_bridge_name(self._pif)
447 dpname = pif_bridge_name(self._dp)
449 if pif_is_vlan(self._pif):
450 # XXX this is only needed on XS5.5, because XAPI misguidedly
451 # creates the fake bridge (via bridge ioctl) before it calls us.
452 vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
454 # configure_datapath() set up the underlying datapath bridge.
455 # Stack a VLAN bridge on top of it.
456 vsctl_argv += ['--', '--may-exist', 'add-br',
457 bridge, dpname, pifrec['VLAN']]
459 vsctl_argv += set_br_external_ids(self._pif)
462 vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
463 vsctl_argv += datapath_deconfigure_ipdev(ipdev)
464 vsctl_argv += ["# reconfigure ipdev %s" % ipdev]
465 vsctl_argv += ['--', 'add-port', bridge, ipdev]
468 vsctl_argv += ['# configure Interface MAC']
469 vsctl_argv += ['--', 'set', 'Interface', pif_ipdev_name(self._pif),
470 'MAC=%s' % vsctl_escape(dprec['MAC'])]
472 self._vsctl_argv = vsctl_argv
473 self._extra_ports = extra_ports
475 def bring_down_existing(self):
476 # interface-reconfigure is never explicitly called to down a
477 # bond master. However, when we are called to up a slave it
478 # is implicit that we are destroying the master. Conversely,
479 # when we are called to up a bond is is implicit that we are
480 # taking down the slaves.
482 # This is (only) important in the case where the device being
483 # implicitly taken down uses DHCP. We need to kill the
484 # dhclient process, otherwise performing the inverse operation
485 # later later will fail because ifup will refuse to start a
486 # duplicate dhclient.
487 bond_masters = pif_get_bond_masters(self._pif)
488 for master in bond_masters:
489 log("action_up: bring down bond master %s" % (pif_netdev_name(master)))
490 run_command(["/sbin/ifdown", pif_bridge_name(master)])
492 bond_slaves = pif_get_bond_slaves(self._pif)
493 for slave in bond_slaves:
494 log("action_up: bring down bond slave %s" % (pif_netdev_name(slave)))
495 run_command(["/sbin/ifdown", pif_bridge_name(slave)])
498 # Bring up physical devices. ovs-vswitchd initially enables or
499 # disables bond slaves based on whether carrier is detected
500 # when they are added, and a network device that is down
501 # always reports "no carrier".
502 physical_devices = datapath_get_physical_pifs(self._dp)
504 for p in physical_devices:
505 prec = db().get_pif_record(p)
506 oc = prec['other_config']
508 dev = pif_netdev_name(p)
510 mtu = mtu_setting(prec['network'], "PIF", oc)
514 settings, offload = ethtool_settings(oc)
516 run_command(['/sbin/ethtool', '-s', dev] + settings)
518 run_command(['/sbin/ethtool', '-K', dev] + offload)
520 datapath_modify_config(self._vsctl_argv)
523 for p in self._extra_ports:
524 log("action_up: bring up %s" % p)
527 def bring_down(self):
533 bridge = pif_bridge_name(dp)
535 #nw = db().get_pif_record(self._pif)['network']
536 #nwrec = db().get_network_record(nw)
537 #vsctl_argv += ['# deconfigure network-uuids']
538 #vsctl_argv += ['--del-entry=bridge.%s.network-uuids=%s' % (bridge,nwrec['uuid'])]
540 log("deconfigure ipdev %s on %s" % (ipdev,bridge))
541 vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
542 vsctl_argv += datapath_deconfigure_ipdev(ipdev)
544 if pif_is_vlan(self._pif):
545 # Delete the VLAN bridge.
546 vsctl_argv += deconfigure_bridge(self._pif)
548 # If the VLAN's slave is attached, leave datapath setup.
549 slave = pif_get_vlan_slave(self._pif)
550 if db().get_pif_record(slave)['currently_attached']:
551 log("action_down: vlan slave is currently attached")
554 # If the VLAN's slave has other VLANs that are attached, leave datapath setup.
555 for master in pif_get_vlan_masters(slave):
556 if master != self._pif and db().get_pif_record(master)['currently_attached']:
557 log("action_down: vlan slave has other master: %s" % pif_netdev_name(master))
560 # Otherwise, take down the datapath too (fall through)
562 log("action_down: no more masters, bring down slave %s" % bridge)
564 # Stop here if this PIF has attached VLAN masters.
565 masters = [db().get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(self._pif) if db().get_pif_record(m)['currently_attached']]
567 log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters))
571 vsctl_argv += deconfigure_bridge(dp)
573 physical_devices = [pif_netdev_name(p) for p in datapath_get_physical_pifs(dp)]
575 log("action_down: bring down physical devices - %s" % physical_devices)
577 for p in physical_devices:
580 datapath_modify_config(vsctl_argv)