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 # The use of [!0-9] keeps an interface of 'eth0' from matching
122 # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
124 return ['--del-match=bridge.*.port=%s' % netdev,
125 '--del-match=port.%s.[!0-9]*' % netdev,
126 '--del-match=bonding.*.slave=%s' % netdev,
127 '--del-match=iface.%s.[!0-9]*' % netdev]
129 def datapath_configure_bond(pif,slaves):
130 pifrec = db().get_pif_record(pif)
131 interface = pif_netdev_name(pif)
133 argv = ['--del-match=bonding.%s.[!0-9]*' % interface]
134 argv += ["--add=bonding.%s.slave=%s" % (interface, pif_netdev_name(slave))
136 argv += ['--add=bonding.%s.fake-iface=true' % interface]
138 if pifrec['MAC'] != "":
139 argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
143 "mode": "balance-slb",
149 # override defaults with values from other-config whose keys
151 oc = pifrec['other_config']
152 overrides = filter(lambda (key,val):
153 key.startswith("bond-"), oc.items())
154 overrides = map(lambda (key,val): (key[5:], val), overrides)
155 bond_options.update(overrides)
156 for (name,val) in bond_options.items():
157 argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
160 def datapath_deconfigure_bond(netdev):
161 # The use of [!0-9] keeps an interface of 'eth0' from matching
162 # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
164 return ['--del-match=bonding.%s.[!0-9]*' % netdev,
165 '--del-match=port.%s.[!0-9]*' % netdev]
167 def datapath_deconfigure_ipdev(interface):
168 # The use of [!0-9] keeps an interface of 'eth0' from matching
169 # VLANs attached to eth0 (such as 'eth0.123'), which are distinct
171 return ['--del-match=bridge.*.port=%s' % interface,
172 '--del-match=port.%s.[!0-9]*' % interface,
173 '--del-match=iface.%s.[!0-9]*' % interface,
174 '--del-match=vlan.%s.trunks=*' % interface,
175 '--del-match=vlan.%s.tag=*' % interface]
177 def datapath_modify_config(commands):
178 #log("modifying configuration:")
182 rc = run_command(['/usr/bin/ovs-cfg-mod', '-vANY:console:emer',
183 '-F', '/etc/ovs-vswitchd.conf']
184 + [c for c in commands if c[0] != '#'] + ['-c'])
186 raise Error("Failed to modify vswitch configuration")
187 run_command(['/sbin/service', 'vswitch', 'reload'])
191 # Toplevel Datapath Configuration.
194 def configure_datapath(pif):
195 """Bring up the datapath configuration for PIF.
197 Should be careful not to glitch existing users of the datapath, e.g. other VLANs etc.
199 Should take care of tearing down other PIFs which encompass common physical devices.
201 Returns a tuple containing
202 - A list containing the necessary cfgmod command line arguments
203 - A list of additional devices which should be brought up after
204 the configuration is applied.
210 bridge = pif_bridge_name(pif)
212 physical_devices = datapath_get_physical_pifs(pif)
214 # Determine additional devices to deconfigure.
216 # Given all physical devices which are part of this PIF we need to
218 # - any additional bond which a physical device is part of.
219 # - any additional physical devices which are part of an additional bond.
221 # Any of these which are not currently in use should be brought
222 # down and deconfigured.
223 extra_down_bonds = []
224 extra_down_ports = []
225 for p in physical_devices:
226 for bond in pif_get_bond_masters(p):
228 log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond))
230 if bond in extra_down_bonds:
232 if db().get_pif_record(bond)['currently_attached']:
233 log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond))
235 extra_down_bonds += [bond]
237 for s in pif_get_bond_slaves(bond):
238 if s in physical_devices:
240 if s in extra_down_ports:
242 if pif_currently_in_use(s):
244 extra_down_ports += [s]
246 log("configure_datapath: bridge - %s" % bridge)
247 log("configure_datapath: physical - %s" % [pif_netdev_name(p) for p in physical_devices])
248 log("configure_datapath: extra ports - %s" % [pif_netdev_name(p) for p in extra_down_ports])
249 log("configure_datapath: extra bonds - %s" % [pif_netdev_name(p) for p in extra_down_bonds])
251 # Need to fully deconfigure any bridge which any of the:
256 for brpif in physical_devices + extra_down_ports + extra_down_bonds:
259 b = pif_bridge_name(brpif)
263 cfgmod_argv += ['# remove bridge %s' % b]
264 cfgmod_argv += ['--del-match=bridge.%s.*' % b]
266 for n in extra_down_ports:
267 dev = pif_netdev_name(n)
268 cfgmod_argv += ['# deconfigure sibling physical device %s' % dev]
269 cfgmod_argv += datapath_deconfigure_physical(dev)
272 for n in extra_down_bonds:
273 dev = pif_netdev_name(n)
274 cfgmod_argv += ['# deconfigure bond device %s' % dev]
275 cfgmod_argv += datapath_deconfigure_bond(dev)
278 for p in physical_devices:
279 dev = pif_netdev_name(p)
280 cfgmod_argv += ['# deconfigure physical port %s' % dev]
281 cfgmod_argv += datapath_deconfigure_physical(dev)
282 if len(physical_devices) > 1:
283 cfgmod_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
284 cfgmod_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
285 cfgmod_argv += ['--del-entry=bridge.%s.port=%s' % (bridge,pif_netdev_name(pif))]
286 cfgmod_argv += ['# configure bond %s' % pif_netdev_name(pif)]
287 cfgmod_argv += datapath_configure_bond(pif, physical_devices)
288 cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge,pif_netdev_name(pif)) ]
289 extra_up_ports += [pif_netdev_name(pif)]
291 iface = pif_netdev_name(physical_devices[0])
292 cfgmod_argv += ['# add physical device %s' % iface]
293 cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge,iface) ]
295 return cfgmod_argv,extra_up_ports
297 def deconfigure_datapath(pif):
300 bridge = pif_bridge_name(pif)
302 physical_devices = datapath_get_physical_pifs(pif)
304 log("deconfigure_datapath: bridge - %s" % bridge)
305 log("deconfigure_datapath: physical devices - %s" % [pif_netdev_name(p) for p in physical_devices])
307 for p in physical_devices:
308 dev = pif_netdev_name(p)
309 cfgmod_argv += ['# deconfigure physical port %s' % dev]
310 cfgmod_argv += datapath_deconfigure_physical(dev)
313 if len(physical_devices) > 1:
314 cfgmod_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
315 cfgmod_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
317 cfgmod_argv += ['# deconfigure bridge %s' % bridge]
318 cfgmod_argv += ['--del-match=bridge.%s.*' % bridge]
326 class DatapathVswitch(Datapath):
327 def __init__(self, pif):
328 Datapath.__init__(self, pif)
329 self._dp = pif_datapath(pif)
330 self._ipdev = pif_ipdev_name(pif)
332 if pif_is_vlan(pif) and not self._dp:
333 raise Error("Unbridged VLAN devices not implemented yet")
335 log("Configured for Vswitch datapath")
337 def configure_ipdev(self, cfg):
338 cfg.write("TYPE=Ethernet\n")
340 def preconfigure(self, parent):
344 pifrec = db().get_pif_record(self._pif)
347 bridge = pif_bridge_name(self._dp)
348 c,e = configure_datapath(self._dp)
352 cfgmod_argv += ['# configure xs-network-uuids']
353 cfgmod_argv += ['--del-match=bridge.%s.xs-network-uuids=*' % bridge]
355 for nwpif in db().get_pifs_by_device(db().get_pif_record(self._pif)['device']):
356 rec = db().get_pif_record(nwpif)
358 # When state is read from dbcache PIF.currently_attached
359 # is always assumed to be false... Err on the side of
360 # listing even detached networks for the time being.
361 #if nwpif != pif and not rec['currently_attached']:
362 # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
364 nwrec = db().get_network_record(rec['network'])
365 cfgmod_argv += ['--add=bridge.%s.xs-network-uuids=%s' % (bridge, nwrec['uuid'])]
367 cfgmod_argv += ["# deconfigure ipdev %s" % ipdev]
368 cfgmod_argv += datapath_deconfigure_ipdev(ipdev)
369 cfgmod_argv += ["# reconfigure ipdev %s" % ipdev]
370 cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge, ipdev)]
372 cfgmod_argv += ['--add=bridge.%s.mac=%s' % (bridge, pifrec['MAC'])]
374 cfgmod_argv += ['--add=iface.%s.mac=%s' % (ipdev, pifrec['MAC'])]
376 if pif_is_vlan(self._pif):
377 cfgmod_argv += ['--add=vlan.%s.tag=%s' % (ipdev, pifrec['VLAN'])]
378 cfgmod_argv += ['--add=iface.%s.internal=true' % (ipdev)]
379 cfgmod_argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)]
381 self._cfgmod_argv = cfgmod_argv
382 self._extra_ports = extra_ports
384 def bring_down_existing(self):
388 # Bring up physical devices. ovs-vswitchd initially enables or
389 # disables bond slaves based on whether carrier is detected
390 # when they are added, and a network device that is down
391 # always reports "no carrier".
392 physical_devices = datapath_get_physical_pifs(self._dp)
394 for p in physical_devices:
395 oc = db().get_pif_record(p)['other_config']
397 dev = pif_netdev_name(p)
399 mtu = mtu_setting(oc)
403 settings, offload = ethtool_settings(oc)
405 run_command(['/sbin/ethtool', '-s', dev] + settings)
407 run_command(['/sbin/ethtool', '-K', dev] + offload)
409 datapath_modify_config(self._cfgmod_argv)
412 for p in self._extra_ports:
413 log("action_up: bring up %s" % p)
416 def bring_down(self):
422 bridge = pif_bridge_name(dp)
424 #nw = db().get_pif_record(self._pif)['network']
425 #nwrec = db().get_network_record(nw)
426 #cfgmod_argv += ['# deconfigure xs-network-uuids']
427 #cfgmod_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])]
429 log("deconfigure ipdev %s on %s" % (ipdev,bridge))
430 cfgmod_argv += ["# deconfigure ipdev %s" % ipdev]
431 cfgmod_argv += datapath_deconfigure_ipdev(ipdev)
433 if pif_is_vlan(self._pif):
434 # If the VLAN's slave is attached, leave datapath setup.
435 slave = pif_get_vlan_slave(self._pif)
436 if db().get_pif_record(slave)['currently_attached']:
437 log("action_down: vlan slave is currently attached")
440 # If the VLAN's slave has other VLANs that are attached, leave datapath setup.
441 for master in pif_get_vlan_masters(slave):
442 if master != self._pif and db().get_pif_record(master)['currently_attached']:
443 log("action_down: vlan slave has other master: %s" % pif_netdev_name(master))
446 # Otherwise, take down the datapath too (fall through)
448 log("action_down: no more masters, bring down slave %s" % bridge)
450 # Stop here if this PIF has attached VLAN masters.
451 masters = [db().get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(self._pif) if db().get_pif_record(m)['currently_attached']]
453 log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters))
457 cfgmod_argv += deconfigure_datapath(dp)
458 datapath_modify_config(cfgmod_argv)