]> git.proxmox.com Git - ovs.git/blob - xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py
vswitchd: Make names of Bridge external_ids generic.
[ovs.git] / xenserver / opt_xensource_libexec_InterfaceReconfigureVswitch.py
1 # Copyright (c) 2008,2009 Citrix Systems, Inc.
2 # Copyright (c) 2009,2010 Nicira Networks.
3 #
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.
8 #
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.
13 #
14 from InterfaceReconfigure import *
15 import re
16
17 #
18 # Bare Network Devices -- network devices without IP configuration
19 #
20
21 def netdev_down(netdev):
22 """Bring down a bare network device"""
23 if not netdev_exists(netdev):
24 log("netdev: down: device %s does not exist, ignoring" % netdev)
25 return
26 run_command(["/sbin/ifconfig", netdev, 'down'])
27
28 def netdev_up(netdev, mtu=None):
29 """Bring up a bare network device"""
30 if not netdev_exists(netdev):
31 raise Error("netdev: up: device %s does not exist" % netdev)
32
33 if mtu:
34 mtu = ["mtu", mtu]
35 else:
36 mtu = []
37
38 run_command(["/sbin/ifconfig", netdev, 'up'] + mtu)
39
40 #
41 # PIF miscellanea
42 #
43
44 def pif_currently_in_use(pif):
45 """Determine if a PIF is currently in use.
46
47 A PIF is determined to be currently in use if
48 - PIF.currently-attached is true
49 - Any bond master is currently attached
50 - Any VLAN master is currently attached
51 """
52 rec = db().get_pif_record(pif)
53 if rec['currently_attached']:
54 log("configure_datapath: %s is currently attached" % (pif_netdev_name(pif)))
55 return True
56 for b in pif_get_bond_masters(pif):
57 if pif_currently_in_use(b):
58 log("configure_datapath: %s is in use by BOND master %s" % (pif_netdev_name(pif),pif_netdev_name(b)))
59 return True
60 for v in pif_get_vlan_masters(pif):
61 if pif_currently_in_use(v):
62 log("configure_datapath: %s is in use by VLAN master %s" % (pif_netdev_name(pif),pif_netdev_name(v)))
63 return True
64 return False
65
66 #
67 # Datapath Configuration
68 #
69
70 def pif_datapath(pif):
71 """Return the datapath PIF associated with PIF.
72 A non-VLAN PIF is its own datapath PIF, except that a bridgeless PIF has
73 no datapath PIF at all.
74 A VLAN PIF's datapath PIF is its VLAN slave's datapath PIF.
75 """
76 if pif_is_vlan(pif):
77 return pif_datapath(pif_get_vlan_slave(pif))
78
79 pifrec = db().get_pif_record(pif)
80 nwrec = db().get_network_record(pifrec['network'])
81 if not nwrec['bridge']:
82 return None
83 else:
84 return pif
85
86 def datapath_get_physical_pifs(pif):
87 """Return the PIFs for the physical network device(s) associated with a datapath PIF.
88 For a bond master PIF, these are the bond slave PIFs.
89 For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
90
91 A VLAN PIF cannot be a datapath PIF.
92 """
93 if pif_is_vlan(pif):
94 # Seems like overkill...
95 raise Error("get-physical-pifs should not get passed a VLAN")
96 elif pif_is_bond(pif):
97 return pif_get_bond_slaves(pif)
98 else:
99 return [pif]
100
101 def datapath_deconfigure_physical(netdev):
102 return ['--', '--with-iface', '--if-exists', 'del-port', netdev]
103
104 def vsctl_escape(s):
105 if s.isalnum():
106 return s
107
108 def escape(match):
109 c = match.group(0)
110 if c == '\0':
111 raise Error("strings may not contain null bytes")
112 elif c == '\\':
113 return r'\\'
114 elif c == '\n':
115 return r'\n'
116 elif c == '\r':
117 return r'\r'
118 elif c == '\t':
119 return r'\t'
120 elif c == '\b':
121 return r'\b'
122 elif c == '\a':
123 return r'\a'
124 else:
125 return r'\x%02x' % ord(c)
126 return '"' + re.sub(r'["\\\000-\037]', escape, s) + '"'
127
128 def datapath_configure_bond(pif,slaves):
129 bridge = pif_bridge_name(pif)
130 pifrec = db().get_pif_record(pif)
131 interface = pif_netdev_name(pif)
132
133 argv = ['--', '--fake-iface', 'add-bond', bridge, interface]
134 for slave in slaves:
135 argv += [pif_netdev_name(slave)]
136
137 # Bonding options.
138 bond_options = {
139 "mode": "balance-slb",
140 "miimon": "100",
141 "downdelay": "200",
142 "updelay": "31000",
143 "use_carrier": "1",
144 }
145 # override defaults with values from other-config whose keys
146 # being with "bond-"
147 oc = pifrec['other_config']
148 overrides = filter(lambda (key,val):
149 key.startswith("bond-"), oc.items())
150 overrides = map(lambda (key,val): (key[5:], val), overrides)
151 bond_options.update(overrides)
152
153 argv += ['--', 'set', 'Port', interface]
154 if pifrec['MAC'] != "":
155 argv += ['MAC=%s' % vsctl_escape(pifrec['MAC'])]
156 for (name,val) in bond_options.items():
157 if name in ['updelay', 'downdelay']:
158 # updelay and downdelay have dedicated schema columns.
159 # The value must be a nonnegative integer.
160 try:
161 value = int(val)
162 if value < 0:
163 raise ValueError
164
165 argv += ['bond_%s=%d' % (name, value)]
166 except ValueError:
167 log("bridge %s has invalid %s '%s'" % (bridge, name, value))
168 else:
169 # Pass other bond options into other_config.
170 argv += ["other-config:%s=%s" % (vsctl_escape("bond-%s" % name),
171 vsctl_escape(val))]
172 return argv
173
174 def datapath_deconfigure_bond(netdev):
175 return ['--', '--with-iface', '--if-exists', 'del-port', netdev]
176
177 def datapath_deconfigure_ipdev(interface):
178 return ['--', '--with-iface', '--if-exists', 'del-port', interface]
179
180 def datapath_modify_config(commands):
181 #log("modifying configuration:")
182 #for c in commands:
183 # log(" %s" % c)
184
185 rc = run_command(['/usr/bin/ovs-vsctl'] + ['--timeout=20']
186 + [c for c in commands if not c.startswith('#')])
187 if not rc:
188 raise Error("Failed to modify vswitch configuration")
189 return True
190
191 #
192 # Toplevel Datapath Configuration.
193 #
194
195 def configure_datapath(pif):
196 """Bring up the configuration for 'pif', which must not be a VLAN PIF, by:
197 - Tearing down other PIFs that use the same physical devices as 'pif'.
198 - Ensuring that 'pif' itself is set up.
199 - *Not* tearing down any PIFs that are stacked on top of 'pif' (i.e. VLANs
200 on top of 'pif'.
201
202 Returns a tuple containing
203 - A list containing the necessary vsctl command line arguments
204 - A list of additional devices which should be brought up after
205 the configuration is applied.
206 """
207
208 vsctl_argv = []
209 extra_up_ports = []
210
211 assert not pif_is_vlan(pif)
212 bridge = pif_bridge_name(pif)
213
214 physical_devices = datapath_get_physical_pifs(pif)
215
216 vsctl_argv += ['## configuring datapath %s' % bridge]
217
218 # Determine additional devices to deconfigure.
219 #
220 # Given all physical devices which are part of this PIF we need to
221 # consider:
222 # - any additional bond which a physical device is part of.
223 # - any additional physical devices which are part of an additional bond.
224 #
225 # Any of these which are not currently in use should be brought
226 # down and deconfigured.
227 extra_down_bonds = []
228 extra_down_ports = []
229 for p in physical_devices:
230 for bond in pif_get_bond_masters(p):
231 if bond == pif:
232 log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond))
233 continue
234 if bond in extra_down_bonds:
235 continue
236 if db().get_pif_record(bond)['currently_attached']:
237 log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond))
238
239 extra_down_bonds += [bond]
240
241 for s in pif_get_bond_slaves(bond):
242 if s in physical_devices:
243 continue
244 if s in extra_down_ports:
245 continue
246 if pif_currently_in_use(s):
247 continue
248 extra_down_ports += [s]
249
250 log("configure_datapath: bridge - %s" % bridge)
251 log("configure_datapath: physical - %s" % [pif_netdev_name(p) for p in physical_devices])
252 log("configure_datapath: extra ports - %s" % [pif_netdev_name(p) for p in extra_down_ports])
253 log("configure_datapath: extra bonds - %s" % [pif_netdev_name(p) for p in extra_down_bonds])
254
255 # Need to fully deconfigure any bridge which any of the:
256 # - physical devices
257 # - bond devices
258 # - sibling devices
259 # refers to
260 for brpif in physical_devices + extra_down_ports + extra_down_bonds:
261 if brpif == pif:
262 continue
263 b = pif_bridge_name(brpif)
264 #ifdown(b)
265 # XXX
266 netdev_down(b)
267 vsctl_argv += ['# remove bridge %s' % b]
268 vsctl_argv += ['--', '--if-exists', 'del-br', b]
269
270 for n in extra_down_ports:
271 dev = pif_netdev_name(n)
272 vsctl_argv += ['# deconfigure sibling physical device %s' % dev]
273 vsctl_argv += datapath_deconfigure_physical(dev)
274 netdev_down(dev)
275
276 for n in extra_down_bonds:
277 dev = pif_netdev_name(n)
278 vsctl_argv += ['# deconfigure bond device %s' % dev]
279 vsctl_argv += datapath_deconfigure_bond(dev)
280 netdev_down(dev)
281
282 for p in physical_devices:
283 dev = pif_netdev_name(p)
284 vsctl_argv += ['# deconfigure physical port %s' % dev]
285 vsctl_argv += datapath_deconfigure_physical(dev)
286
287 vsctl_argv += ['--', '--may-exist', 'add-br', bridge]
288
289 if len(physical_devices) > 1:
290 vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
291 vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
292 vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)]
293 vsctl_argv += datapath_configure_bond(pif, physical_devices)
294 extra_up_ports += [pif_netdev_name(pif)]
295 else:
296 iface = pif_netdev_name(physical_devices[0])
297 vsctl_argv += ['# add physical device %s' % iface]
298 vsctl_argv += ['--', '--may-exist', 'add-port', bridge, iface]
299
300 vsctl_argv += set_br_external_ids(pif)
301 vsctl_argv += ['## done configuring datapath %s' % bridge]
302
303 return vsctl_argv,extra_up_ports
304
305 def deconfigure_bridge(pif):
306 vsctl_argv = []
307
308 bridge = pif_bridge_name(pif)
309
310 log("deconfigure_bridge: bridge - %s" % bridge)
311
312 vsctl_argv += ['# deconfigure bridge %s' % bridge]
313 vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
314
315 return vsctl_argv
316
317 def set_br_external_ids(pif):
318 pifrec = db().get_pif_record(pif)
319 dp = pif_datapath(pif)
320 dprec = db().get_pif_record(dp)
321
322 xs_network_uuids = []
323 for nwpif in db().get_pifs_by_device(pifrec['device']):
324 rec = db().get_pif_record(nwpif)
325
326 # When state is read from dbcache PIF.currently_attached
327 # is always assumed to be false... Err on the side of
328 # listing even detached networks for the time being.
329 #if nwpif != pif and not rec['currently_attached']:
330 # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
331 # continue
332 nwrec = db().get_network_record(rec['network'])
333 xs_network_uuids += [nwrec['uuid']]
334
335 vsctl_argv = []
336 vsctl_argv += ['# configure network-uuids']
337 vsctl_argv += ['--', 'br-set-external-id', pif_bridge_name(pif),
338 'network-uuids', ';'.join(xs_network_uuids)]
339
340 vsctl_argv += ['# configure MAC']
341 vsctl_argv += ['--', 'set', 'Interface', pif_ipdev_name(pif),
342 'MAC=%s' % vsctl_escape(dprec['MAC'])]
343
344 return vsctl_argv
345
346 #
347 #
348 #
349
350 class DatapathVswitch(Datapath):
351 def __init__(self, pif):
352 Datapath.__init__(self, pif)
353 self._dp = pif_datapath(pif)
354 self._ipdev = pif_ipdev_name(pif)
355
356 if pif_is_vlan(pif) and not self._dp:
357 raise Error("Unbridged VLAN devices not implemented yet")
358
359 log("Configured for Vswitch datapath")
360
361 def configure_ipdev(self, cfg):
362 cfg.write("TYPE=Ethernet\n")
363
364 def preconfigure(self, parent):
365 vsctl_argv = []
366 extra_ports = []
367
368 pifrec = db().get_pif_record(self._pif)
369 dprec = db().get_pif_record(self._dp)
370
371 ipdev = self._ipdev
372 c,e = configure_datapath(self._dp)
373 bridge = pif_bridge_name(self._pif)
374 vsctl_argv += c
375 extra_ports += e
376
377 if pif_is_vlan(self._pif):
378 # XXX this is only needed on XS5.5, because XAPI misguidedly
379 # creates the fake bridge (via bridge ioctl) before it calls us.
380 vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
381
382 # configure_datapath() set up the underlying datapath bridge.
383 # Stack a VLAN bridge on top of it.
384 vsctl_argv += ['--', '--may-exist', 'add-br',
385 bridge, pif_bridge_name(self._dp), pifrec['VLAN']]
386
387 vsctl_argv += set_br_external_ids(self._pif)
388
389 if ipdev != bridge:
390 vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
391 vsctl_argv += datapath_deconfigure_ipdev(ipdev)
392 vsctl_argv += ["# reconfigure ipdev %s" % ipdev]
393 vsctl_argv += ['--', 'add-port', bridge, ipdev]
394
395 self._vsctl_argv = vsctl_argv
396 self._extra_ports = extra_ports
397
398 def bring_down_existing(self):
399 pass
400
401 def configure(self):
402 # Bring up physical devices. ovs-vswitchd initially enables or
403 # disables bond slaves based on whether carrier is detected
404 # when they are added, and a network device that is down
405 # always reports "no carrier".
406 physical_devices = datapath_get_physical_pifs(self._dp)
407
408 for p in physical_devices:
409 prec = db().get_pif_record(p)
410 oc = prec['other_config']
411
412 dev = pif_netdev_name(p)
413
414 mtu = mtu_setting(prec['network'], "PIF", oc)
415
416 netdev_up(dev, mtu)
417
418 settings, offload = ethtool_settings(oc)
419 if len(settings):
420 run_command(['/sbin/ethtool', '-s', dev] + settings)
421 if len(offload):
422 run_command(['/sbin/ethtool', '-K', dev] + offload)
423
424 datapath_modify_config(self._vsctl_argv)
425
426 def post(self):
427 for p in self._extra_ports:
428 log("action_up: bring up %s" % p)
429 netdev_up(p)
430
431 def bring_down(self):
432 vsctl_argv = []
433
434 dp = self._dp
435 ipdev = self._ipdev
436
437 bridge = pif_bridge_name(dp)
438
439 #nw = db().get_pif_record(self._pif)['network']
440 #nwrec = db().get_network_record(nw)
441 #vsctl_argv += ['# deconfigure network-uuids']
442 #vsctl_argv += ['--del-entry=bridge.%s.network-uuids=%s' % (bridge,nwrec['uuid'])]
443
444 log("deconfigure ipdev %s on %s" % (ipdev,bridge))
445 vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
446 vsctl_argv += datapath_deconfigure_ipdev(ipdev)
447
448 if pif_is_vlan(self._pif):
449 # Delete the VLAN bridge.
450 vsctl_argv += deconfigure_bridge(self._pif)
451
452 # If the VLAN's slave is attached, leave datapath setup.
453 slave = pif_get_vlan_slave(self._pif)
454 if db().get_pif_record(slave)['currently_attached']:
455 log("action_down: vlan slave is currently attached")
456 dp = None
457
458 # If the VLAN's slave has other VLANs that are attached, leave datapath setup.
459 for master in pif_get_vlan_masters(slave):
460 if master != self._pif and db().get_pif_record(master)['currently_attached']:
461 log("action_down: vlan slave has other master: %s" % pif_netdev_name(master))
462 dp = None
463
464 # Otherwise, take down the datapath too (fall through)
465 if dp:
466 log("action_down: no more masters, bring down slave %s" % bridge)
467 else:
468 # Stop here if this PIF has attached VLAN masters.
469 masters = [db().get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(self._pif) if db().get_pif_record(m)['currently_attached']]
470 if len(masters) > 0:
471 log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters))
472 dp = None
473
474 if dp:
475 vsctl_argv += deconfigure_bridge(dp)
476
477 physical_devices = [pif_netdev_name(p) for p in datapath_get_physical_pifs(dp)]
478
479 log("action_down: bring down physical devices - %s" % physical_devices)
480
481 for p in physical_devices:
482 netdev_down(p)
483
484 datapath_modify_config(vsctl_argv)