]>
Commit | Line | Data |
---|---|---|
431488e6 | 1 | # Copyright (c) 2008,2009,2011 Citrix Systems, Inc. |
0b2c7e69 | 2 | # Copyright (c) 2009,2010,2011,2012,2013,2017 Nicira, Inc. |
b3080599 IC |
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 * | |
ab32de00 | 15 | import os |
47a3c536 | 16 | import re |
da7198b4 | 17 | import subprocess |
b3080599 IC |
18 | |
19 | # | |
20 | # Bare Network Devices -- network devices without IP configuration | |
21 | # | |
22 | ||
23 | def netdev_down(netdev): | |
24 | """Bring down a bare network device""" | |
25 | if not netdev_exists(netdev): | |
26 | log("netdev: down: device %s does not exist, ignoring" % netdev) | |
27 | return | |
0b2c7e69 | 28 | run_command(["/sbin/ip", "link", "set", netdev, 'down']) |
b3080599 IC |
29 | |
30 | def netdev_up(netdev, mtu=None): | |
31 | """Bring up a bare network device""" | |
32 | if not netdev_exists(netdev): | |
33 | raise Error("netdev: up: device %s does not exist" % netdev) | |
34 | ||
35 | if mtu: | |
36 | mtu = ["mtu", mtu] | |
37 | else: | |
38 | mtu = [] | |
39 | ||
0b2c7e69 | 40 | run_command(["/sbin/ip", "link", "set", netdev, 'up'] + mtu) |
b3080599 | 41 | |
431488e6 BP |
42 | # This is a list of drivers that do support VLAN tx or rx acceleration, but |
43 | # to which the VLAN bug workaround should not be applied. This could be | |
44 | # because these are known-good drivers (that is, they do not have any of | |
45 | # the bugs that the workaround avoids) or because the VLAN bug workaround | |
46 | # will not work for them and may cause other problems. | |
47 | # | |
48 | # This is a very short list because few drivers have been tested. | |
49 | NO_VLAN_WORKAROUND_DRIVERS = ( | |
50 | "bonding", | |
51 | ) | |
52 | def netdev_get_driver_name(netdev): | |
53 | """Returns the name of the driver for network device 'netdev'""" | |
54 | symlink = '%s/sys/class/net/%s/device/driver' % (root_prefix(), netdev) | |
55 | try: | |
56 | target = os.readlink(symlink) | |
fc35b165 | 57 | except OSError as e: |
431488e6 BP |
58 | log("%s: could not read netdev's driver name (%s)" % (netdev, e)) |
59 | return None | |
60 | ||
61 | slash = target.rfind('/') | |
62 | if slash < 0: | |
63 | log("target %s of symbolic link %s does not contain slash" | |
64 | % (target, symlink)) | |
65 | return None | |
66 | ||
67 | return target[slash + 1:] | |
68 | ||
69 | def netdev_get_features(netdev): | |
70 | """Returns the features bitmap for the driver for 'netdev'. | |
71 | The features bitmap is a set of NETIF_F_ flags supported by its driver.""" | |
72 | try: | |
73 | features = open("%s/sys/class/net/%s/features" % (root_prefix(), netdev)).read().strip() | |
74 | return int(features, 0) | |
75 | except: | |
76 | return 0 # interface prolly doesn't exist | |
77 | ||
78 | def netdev_has_vlan_accel(netdev): | |
79 | """Returns True if 'netdev' supports VLAN acceleration, False otherwise.""" | |
80 | NETIF_F_HW_VLAN_TX = 128 | |
81 | NETIF_F_HW_VLAN_RX = 256 | |
82 | NETIF_F_VLAN = NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX | |
83 | return (netdev_get_features(netdev) & NETIF_F_VLAN) != 0 | |
84 | ||
b3080599 IC |
85 | # |
86 | # PIF miscellanea | |
87 | # | |
88 | ||
89 | def pif_currently_in_use(pif): | |
90 | """Determine if a PIF is currently in use. | |
91 | ||
92 | A PIF is determined to be currently in use if | |
93 | - PIF.currently-attached is true | |
94 | - Any bond master is currently attached | |
95 | - Any VLAN master is currently attached | |
96 | """ | |
97 | rec = db().get_pif_record(pif) | |
98 | if rec['currently_attached']: | |
99 | log("configure_datapath: %s is currently attached" % (pif_netdev_name(pif))) | |
100 | return True | |
101 | for b in pif_get_bond_masters(pif): | |
102 | if pif_currently_in_use(b): | |
103 | log("configure_datapath: %s is in use by BOND master %s" % (pif_netdev_name(pif),pif_netdev_name(b))) | |
104 | return True | |
105 | for v in pif_get_vlan_masters(pif): | |
106 | if pif_currently_in_use(v): | |
107 | log("configure_datapath: %s is in use by VLAN master %s" % (pif_netdev_name(pif),pif_netdev_name(v))) | |
108 | return True | |
109 | return False | |
110 | ||
111 | # | |
112 | # Datapath Configuration | |
113 | # | |
114 | ||
115 | def pif_datapath(pif): | |
116 | """Return the datapath PIF associated with PIF. | |
3b1acc99 BP |
117 | A non-VLAN PIF is its own datapath PIF, except that a bridgeless PIF has |
118 | no datapath PIF at all. | |
119 | A VLAN PIF's datapath PIF is its VLAN slave's datapath PIF. | |
b3080599 IC |
120 | """ |
121 | if pif_is_vlan(pif): | |
122 | return pif_datapath(pif_get_vlan_slave(pif)) | |
123 | ||
124 | pifrec = db().get_pif_record(pif) | |
125 | nwrec = db().get_network_record(pifrec['network']) | |
126 | if not nwrec['bridge']: | |
127 | return None | |
128 | else: | |
129 | return pif | |
130 | ||
131 | def datapath_get_physical_pifs(pif): | |
132 | """Return the PIFs for the physical network device(s) associated with a datapath PIF. | |
133 | For a bond master PIF, these are the bond slave PIFs. | |
134 | For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF. | |
135 | ||
136 | A VLAN PIF cannot be a datapath PIF. | |
137 | """ | |
92e906e4 IC |
138 | if pif_is_tunnel(pif): |
139 | return [] | |
140 | elif pif_is_vlan(pif): | |
b3080599 IC |
141 | # Seems like overkill... |
142 | raise Error("get-physical-pifs should not get passed a VLAN") | |
143 | elif pif_is_bond(pif): | |
144 | return pif_get_bond_slaves(pif) | |
145 | else: | |
146 | return [pif] | |
147 | ||
148 | def datapath_deconfigure_physical(netdev): | |
7c79588e | 149 | return ['--', '--with-iface', '--if-exists', 'del-port', netdev] |
b3080599 | 150 | |
47a3c536 BP |
151 | def vsctl_escape(s): |
152 | if s.isalnum(): | |
153 | return s | |
154 | ||
155 | def escape(match): | |
156 | c = match.group(0) | |
157 | if c == '\0': | |
158 | raise Error("strings may not contain null bytes") | |
159 | elif c == '\\': | |
160 | return r'\\' | |
161 | elif c == '\n': | |
162 | return r'\n' | |
163 | elif c == '\r': | |
164 | return r'\r' | |
165 | elif c == '\t': | |
166 | return r'\t' | |
167 | elif c == '\b': | |
168 | return r'\b' | |
169 | elif c == '\a': | |
170 | return r'\a' | |
171 | else: | |
172 | return r'\x%02x' % ord(c) | |
173 | return '"' + re.sub(r'["\\\000-\037]', escape, s) + '"' | |
174 | ||
92e906e4 IC |
175 | def datapath_configure_tunnel(pif): |
176 | pass | |
177 | ||
b3080599 | 178 | def datapath_configure_bond(pif,slaves): |
a4af0040 | 179 | bridge = pif_bridge_name(pif) |
e91087c7 | 180 | pifrec = db().get_pif_record(pif) |
b3080599 IC |
181 | interface = pif_netdev_name(pif) |
182 | ||
a4af0040 JP |
183 | argv = ['--', '--fake-iface', 'add-bond', bridge, interface] |
184 | for slave in slaves: | |
185 | argv += [pif_netdev_name(slave)] | |
b3080599 | 186 | |
b3080599 IC |
187 | # Bonding options. |
188 | bond_options = { | |
189 | "mode": "balance-slb", | |
190 | "miimon": "100", | |
191 | "downdelay": "200", | |
192 | "updelay": "31000", | |
193 | "use_carrier": "1", | |
2776e408 | 194 | "hashing-algorithm": "src_mac", |
b3080599 IC |
195 | } |
196 | # override defaults with values from other-config whose keys | |
197 | # being with "bond-" | |
198 | oc = pifrec['other_config'] | |
cb1e1737 JS |
199 | overrides = filter(lambda key_val: |
200 | key_val[0].startswith("bond-"), oc.items()) | |
201 | overrides = map(lambda key_val: (key_val[0][5:], key_val[1]), overrides) | |
b3080599 | 202 | bond_options.update(overrides) |
2776e408 EJ |
203 | mode = None |
204 | halgo = None | |
47a3c536 BP |
205 | |
206 | argv += ['--', 'set', 'Port', interface] | |
207 | if pifrec['MAC'] != "": | |
208 | argv += ['MAC=%s' % vsctl_escape(pifrec['MAC'])] | |
8b35f045 | 209 | for (name,val) in sorted(bond_options.items()): |
47a3c536 BP |
210 | if name in ['updelay', 'downdelay']: |
211 | # updelay and downdelay have dedicated schema columns. | |
212 | # The value must be a nonnegative integer. | |
213 | try: | |
214 | value = int(val) | |
215 | if value < 0: | |
216 | raise ValueError | |
217 | ||
218 | argv += ['bond_%s=%d' % (name, value)] | |
219 | except ValueError: | |
220 | log("bridge %s has invalid %s '%s'" % (bridge, name, value)) | |
b9b627d5 EJ |
221 | elif name in ['miimon', 'use_carrier']: |
222 | try: | |
223 | value = int(val) | |
224 | if value < 0: | |
225 | raise ValueError | |
226 | ||
227 | if name == 'use_carrier': | |
228 | if value: | |
229 | value = "carrier" | |
230 | else: | |
231 | value = "miimon" | |
232 | argv += ["other-config:bond-detect-mode=%s" % value] | |
233 | else: | |
234 | argv += ["other-config:bond-miimon-interval=%d" % value] | |
235 | except ValueError: | |
236 | log("bridge %s has invalid %s '%s'" % (bridge, name, value)) | |
aa98d0cd | 237 | elif name == "mode": |
2776e408 EJ |
238 | mode = val |
239 | elif name == "hashing-algorithm": | |
240 | halgo = val | |
47a3c536 BP |
241 | else: |
242 | # Pass other bond options into other_config. | |
243 | argv += ["other-config:%s=%s" % (vsctl_escape("bond-%s" % name), | |
244 | vsctl_escape(val))] | |
2776e408 EJ |
245 | |
246 | if mode == 'lacp': | |
247 | argv += ['lacp=active'] | |
248 | ||
249 | if halgo == 'src_mac': | |
250 | argv += ['bond_mode=balance-slb'] | |
251 | elif halgo == "tcpudp_ports": | |
252 | argv += ['bond_mode=balance-tcp'] | |
253 | else: | |
254 | log("bridge %s has invalid bond-hashing-algorithm '%s'" % (bridge, halgo)) | |
255 | argv += ['bond_mode=balance-slb'] | |
256 | elif mode in ['balance-slb', 'active-backup']: | |
257 | argv += ['lacp=off', 'bond_mode=%s' % mode] | |
258 | else: | |
259 | log("bridge %s has invalid bond-mode '%s'" % (bridge, mode)) | |
260 | argv += ['lacp=off', 'bond_mode=balance-slb'] | |
261 | ||
b3080599 IC |
262 | return argv |
263 | ||
264 | def datapath_deconfigure_bond(netdev): | |
7c79588e | 265 | return ['--', '--with-iface', '--if-exists', 'del-port', netdev] |
b3080599 IC |
266 | |
267 | def datapath_deconfigure_ipdev(interface): | |
7c79588e | 268 | return ['--', '--with-iface', '--if-exists', 'del-port', interface] |
b3080599 IC |
269 | |
270 | def datapath_modify_config(commands): | |
13ffee26 JP |
271 | #log("modifying configuration:") |
272 | #for c in commands: | |
273 | # log(" %s" % c) | |
a4af0040 JP |
274 | |
275 | rc = run_command(['/usr/bin/ovs-vsctl'] + ['--timeout=20'] | |
276 | + [c for c in commands if not c.startswith('#')]) | |
277 | if not rc: | |
b3080599 | 278 | raise Error("Failed to modify vswitch configuration") |
b3080599 IC |
279 | return True |
280 | ||
281 | # | |
282 | # Toplevel Datapath Configuration. | |
283 | # | |
284 | ||
d77ffabf BP |
285 | def configure_datapath(pif): |
286 | """Bring up the configuration for 'pif', which must not be a VLAN PIF, by: | |
287 | - Tearing down other PIFs that use the same physical devices as 'pif'. | |
288 | - Ensuring that 'pif' itself is set up. | |
289 | - *Not* tearing down any PIFs that are stacked on top of 'pif' (i.e. VLANs | |
290 | on top of 'pif'. | |
b3080599 IC |
291 | |
292 | Returns a tuple containing | |
a4af0040 | 293 | - A list containing the necessary vsctl command line arguments |
b3080599 IC |
294 | - A list of additional devices which should be brought up after |
295 | the configuration is applied. | |
da7198b4 DT |
296 | - A list containing flows to apply to the pif bridge, note that |
297 | port numbers may need to be substituted once ofport is known | |
b3080599 IC |
298 | """ |
299 | ||
a4af0040 | 300 | vsctl_argv = [] |
b3080599 | 301 | extra_up_ports = [] |
da7198b4 | 302 | bridge_flows = [] |
b3080599 | 303 | |
86e1bb44 | 304 | assert not pif_is_vlan(pif) |
b3080599 IC |
305 | bridge = pif_bridge_name(pif) |
306 | ||
307 | physical_devices = datapath_get_physical_pifs(pif) | |
308 | ||
86e1bb44 BP |
309 | vsctl_argv += ['## configuring datapath %s' % bridge] |
310 | ||
b3080599 IC |
311 | # Determine additional devices to deconfigure. |
312 | # | |
313 | # Given all physical devices which are part of this PIF we need to | |
314 | # consider: | |
315 | # - any additional bond which a physical device is part of. | |
316 | # - any additional physical devices which are part of an additional bond. | |
317 | # | |
318 | # Any of these which are not currently in use should be brought | |
319 | # down and deconfigured. | |
320 | extra_down_bonds = [] | |
321 | extra_down_ports = [] | |
322 | for p in physical_devices: | |
323 | for bond in pif_get_bond_masters(p): | |
324 | if bond == pif: | |
325 | log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond)) | |
326 | continue | |
327 | if bond in extra_down_bonds: | |
328 | continue | |
329 | if db().get_pif_record(bond)['currently_attached']: | |
330 | log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond)) | |
331 | ||
332 | extra_down_bonds += [bond] | |
333 | ||
334 | for s in pif_get_bond_slaves(bond): | |
335 | if s in physical_devices: | |
336 | continue | |
337 | if s in extra_down_ports: | |
338 | continue | |
339 | if pif_currently_in_use(s): | |
340 | continue | |
341 | extra_down_ports += [s] | |
342 | ||
343 | log("configure_datapath: bridge - %s" % bridge) | |
344 | log("configure_datapath: physical - %s" % [pif_netdev_name(p) for p in physical_devices]) | |
345 | log("configure_datapath: extra ports - %s" % [pif_netdev_name(p) for p in extra_down_ports]) | |
346 | log("configure_datapath: extra bonds - %s" % [pif_netdev_name(p) for p in extra_down_bonds]) | |
347 | ||
348 | # Need to fully deconfigure any bridge which any of the: | |
349 | # - physical devices | |
350 | # - bond devices | |
351 | # - sibling devices | |
352 | # refers to | |
353 | for brpif in physical_devices + extra_down_ports + extra_down_bonds: | |
354 | if brpif == pif: | |
355 | continue | |
356 | b = pif_bridge_name(brpif) | |
357 | #ifdown(b) | |
358 | # XXX | |
359 | netdev_down(b) | |
a4af0040 JP |
360 | vsctl_argv += ['# remove bridge %s' % b] |
361 | vsctl_argv += ['--', '--if-exists', 'del-br', b] | |
b3080599 IC |
362 | |
363 | for n in extra_down_ports: | |
364 | dev = pif_netdev_name(n) | |
a4af0040 JP |
365 | vsctl_argv += ['# deconfigure sibling physical device %s' % dev] |
366 | vsctl_argv += datapath_deconfigure_physical(dev) | |
b3080599 IC |
367 | netdev_down(dev) |
368 | ||
369 | for n in extra_down_bonds: | |
370 | dev = pif_netdev_name(n) | |
a4af0040 JP |
371 | vsctl_argv += ['# deconfigure bond device %s' % dev] |
372 | vsctl_argv += datapath_deconfigure_bond(dev) | |
b3080599 IC |
373 | netdev_down(dev) |
374 | ||
375 | for p in physical_devices: | |
376 | dev = pif_netdev_name(p) | |
a4af0040 JP |
377 | vsctl_argv += ['# deconfigure physical port %s' % dev] |
378 | vsctl_argv += datapath_deconfigure_physical(dev) | |
379 | ||
d77ffabf | 380 | vsctl_argv += ['--', '--may-exist', 'add-br', bridge] |
13ffee26 | 381 | |
b3080599 | 382 | if len(physical_devices) > 1: |
a4af0040 JP |
383 | vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)] |
384 | vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif)) | |
385 | vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)] | |
386 | vsctl_argv += datapath_configure_bond(pif, physical_devices) | |
b3080599 | 387 | extra_up_ports += [pif_netdev_name(pif)] |
92e906e4 | 388 | elif len(physical_devices) == 1: |
b3080599 | 389 | iface = pif_netdev_name(physical_devices[0]) |
a4af0040 | 390 | vsctl_argv += ['# add physical device %s' % iface] |
7b09e426 | 391 | vsctl_argv += ['--', '--may-exist', 'add-port', bridge, iface] |
92e906e4 IC |
392 | elif pif_is_tunnel(pif): |
393 | datapath_configure_tunnel(pif) | |
b3080599 | 394 | |
16f2ae57 IC |
395 | vsctl_argv += ['# configure Bridge MAC'] |
396 | vsctl_argv += ['--', 'set', 'Bridge', bridge, | |
397 | 'other-config:hwaddr=%s' % vsctl_escape(db().get_pif_record(pif)['MAC'])] | |
398 | ||
939e5a1b | 399 | pool = db().get_pool_record() |
2dd26837 | 400 | network = db().get_network_by_bridge(bridge) |
da54975c | 401 | network_rec = None |
2dd26837 EJ |
402 | fail_mode = None |
403 | valid_fail_modes = ['standalone', 'secure'] | |
0671665d | 404 | |
2dd26837 EJ |
405 | if network: |
406 | network_rec = db().get_network_record(network) | |
407 | fail_mode = network_rec['other_config'].get('vswitch-controller-fail-mode') | |
408 | ||
409 | if (fail_mode not in valid_fail_modes) and pool: | |
c2875f43 | 410 | fail_mode = pool['other_config'].get('vswitch-controller-fail-mode') |
da7198b4 DT |
411 | # Add default flows to allow management traffic if fail-mode |
412 | # transitions to secure based on pool fail-mode setting | |
413 | if fail_mode == 'secure' and db().get_pif_record(pif).get('management', False): | |
414 | prev_fail_mode = vswitchCfgQuery(['get-fail-mode', bridge]) | |
415 | if prev_fail_mode != 'secure': | |
416 | tp = 'idle_timeout=0,priority=0' | |
417 | host_mgmt_mac = db().get_pif_record(pif)['MAC'] | |
418 | # account for bond as management interface | |
419 | if len(physical_devices) > 1: | |
420 | bridge_flows += ['%s,in_port=local,arp,dl_src=%s,actions=NORMAL' % (tp, host_mgmt_mac)] | |
421 | bridge_flows += ['%s,in_port=local,dl_src=%s,actions=NORMAL' % (tp, host_mgmt_mac)] | |
422 | # we don't know slave ofports yet, substitute later | |
423 | bridge_flows += ['%s,in_port=%%s,arp,nw_proto=1,actions=local' % (tp)] | |
424 | bridge_flows += ['%s,in_port=%%s,dl_dst=%s,actions=local' % (tp, host_mgmt_mac)] | |
425 | else: | |
426 | bridge_flows += ['%s,in_port=%%s,arp,nw_proto=1,actions=local' % (tp)] | |
427 | bridge_flows += ['%s,in_port=local,arp,dl_src=%s,actions=%%s' % (tp, host_mgmt_mac)] | |
428 | bridge_flows += ['%s,in_port=%%s,dl_dst=%s,actions=local' % (tp, host_mgmt_mac)] | |
429 | bridge_flows += ['%s,in_port=local,dl_src=%s,actions=%%s' % (tp, host_mgmt_mac)] | |
2dd26837 EJ |
430 | |
431 | if fail_mode not in valid_fail_modes: | |
0671665d | 432 | fail_mode = 'standalone' |
939e5a1b | 433 | |
2dd26837 | 434 | vsctl_argv += ['--', 'set', 'Bridge', bridge, 'fail_mode=%s' % fail_mode] |
939e5a1b | 435 | |
da54975c AE |
436 | if network_rec: |
437 | dib = network_rec['other_config'].get('vswitch-disable-in-band') | |
438 | if not dib: | |
439 | vsctl_argv += ['--', 'remove', 'Bridge', bridge, 'other_config', 'disable-in-band'] | |
440 | elif dib in ['true', 'false']: | |
441 | vsctl_argv += ['--', 'set', 'Bridge', bridge, 'other_config:disable-in-band=' + dib] | |
442 | else: | |
443 | log('"' + dib + '"' "isn't a valid setting for other_config:disable-in-band on " + bridge) | |
444 | ||
c0a50086 | 445 | vsctl_argv += set_br_external_ids(pif) |
86e1bb44 BP |
446 | vsctl_argv += ['## done configuring datapath %s' % bridge] |
447 | ||
da7198b4 | 448 | return vsctl_argv,extra_up_ports,bridge_flows |
b3080599 | 449 | |
7966caf4 | 450 | def deconfigure_bridge(pif): |
a4af0040 | 451 | vsctl_argv = [] |
b3080599 IC |
452 | |
453 | bridge = pif_bridge_name(pif) | |
454 | ||
2a75efe8 | 455 | log("deconfigure_bridge: bridge - %s" % bridge) |
b3080599 | 456 | |
a4af0040 JP |
457 | vsctl_argv += ['# deconfigure bridge %s' % bridge] |
458 | vsctl_argv += ['--', '--if-exists', 'del-br', bridge] | |
b3080599 | 459 | |
a4af0040 | 460 | return vsctl_argv |
b3080599 | 461 | |
c0a50086 | 462 | def set_br_external_ids(pif): |
47a3c536 BP |
463 | pifrec = db().get_pif_record(pif) |
464 | dp = pif_datapath(pif) | |
465 | dprec = db().get_pif_record(dp) | |
466 | ||
c0a50086 | 467 | xs_network_uuids = [] |
47a3c536 | 468 | for nwpif in db().get_pifs_by_device(pifrec['device']): |
c0a50086 BP |
469 | rec = db().get_pif_record(nwpif) |
470 | ||
471 | # When state is read from dbcache PIF.currently_attached | |
472 | # is always assumed to be false... Err on the side of | |
473 | # listing even detached networks for the time being. | |
474 | #if nwpif != pif and not rec['currently_attached']: | |
475 | # log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid'])) | |
476 | # continue | |
477 | nwrec = db().get_network_record(rec['network']) | |
b13300c7 EJ |
478 | |
479 | uuid = nwrec['uuid'] | |
480 | if pif_is_vlan(nwpif): | |
481 | xs_network_uuids.append(uuid) | |
482 | else: | |
483 | xs_network_uuids.insert(0, uuid) | |
c0a50086 BP |
484 | |
485 | vsctl_argv = [] | |
c76fde78 | 486 | vsctl_argv += ['# configure xs-network-uuids'] |
c0a50086 | 487 | vsctl_argv += ['--', 'br-set-external-id', pif_bridge_name(pif), |
c76fde78 | 488 | 'xs-network-uuids', ';'.join(xs_network_uuids)] |
47a3c536 | 489 | |
c0a50086 BP |
490 | return vsctl_argv |
491 | ||
b3080599 IC |
492 | # |
493 | # | |
494 | # | |
495 | ||
496 | class DatapathVswitch(Datapath): | |
497 | def __init__(self, pif): | |
498 | Datapath.__init__(self, pif) | |
499 | self._dp = pif_datapath(pif) | |
500 | self._ipdev = pif_ipdev_name(pif) | |
da7198b4 | 501 | self._bridge_flows = [] |
b3080599 IC |
502 | |
503 | if pif_is_vlan(pif) and not self._dp: | |
504 | raise Error("Unbridged VLAN devices not implemented yet") | |
505 | ||
506 | log("Configured for Vswitch datapath") | |
507 | ||
823c5699 IC |
508 | @classmethod |
509 | def rewrite(cls): | |
ab32de00 BP |
510 | if not os.path.exists("/var/run/openvswitch/db.sock"): |
511 | # ovsdb-server is not running, so we can't update the database. | |
512 | # Probably we are being called as part of system shutdown. Just | |
513 | # skip the update, since the external-ids will be updated on the | |
514 | # next boot anyhow. | |
515 | return | |
516 | ||
823c5699 IC |
517 | vsctl_argv = [] |
518 | for pif in db().get_all_pifs(): | |
519 | pifrec = db().get_pif_record(pif) | |
520 | if not pif_is_vlan(pif) and pifrec['currently_attached']: | |
521 | vsctl_argv += set_br_external_ids(pif) | |
522 | ||
523 | if vsctl_argv != []: | |
524 | datapath_modify_config(vsctl_argv) | |
525 | ||
b3080599 IC |
526 | def configure_ipdev(self, cfg): |
527 | cfg.write("TYPE=Ethernet\n") | |
528 | ||
529 | def preconfigure(self, parent): | |
a4af0040 | 530 | vsctl_argv = [] |
b3080599 | 531 | extra_ports = [] |
da7198b4 | 532 | bridge_flows = [] |
b3080599 IC |
533 | |
534 | pifrec = db().get_pif_record(self._pif) | |
66aeba23 | 535 | dprec = db().get_pif_record(self._dp) |
b3080599 IC |
536 | |
537 | ipdev = self._ipdev | |
da7198b4 | 538 | c,e,f = configure_datapath(self._dp) |
d77ffabf | 539 | bridge = pif_bridge_name(self._pif) |
a4af0040 | 540 | vsctl_argv += c |
b3080599 | 541 | extra_ports += e |
da7198b4 | 542 | bridge_flows += f |
b3080599 | 543 | |
16f2ae57 IC |
544 | dpname = pif_bridge_name(self._dp) |
545 | ||
d77ffabf | 546 | if pif_is_vlan(self._pif): |
9ec4d255 EJ |
547 | # In some cases XAPI may misguidedly leave an instance of |
548 | # 'bridge' which should be deleted. | |
549 | vsctl_argv += ['--', '--if-exists', 'del-br', bridge] | |
550 | ||
d77ffabf BP |
551 | # configure_datapath() set up the underlying datapath bridge. |
552 | # Stack a VLAN bridge on top of it. | |
553 | vsctl_argv += ['--', '--may-exist', 'add-br', | |
16f2ae57 | 554 | bridge, dpname, pifrec['VLAN']] |
c0a50086 BP |
555 | |
556 | vsctl_argv += set_br_external_ids(self._pif) | |
a4af0040 | 557 | |
13ffee26 JP |
558 | if ipdev != bridge: |
559 | vsctl_argv += ["# deconfigure ipdev %s" % ipdev] | |
560 | vsctl_argv += datapath_deconfigure_ipdev(ipdev) | |
561 | vsctl_argv += ["# reconfigure ipdev %s" % ipdev] | |
562 | vsctl_argv += ['--', 'add-port', bridge, ipdev] | |
a4af0040 | 563 | |
16f2ae57 IC |
564 | if ipdev != dpname: |
565 | vsctl_argv += ['# configure Interface MAC'] | |
566 | vsctl_argv += ['--', 'set', 'Interface', pif_ipdev_name(self._pif), | |
567 | 'MAC=%s' % vsctl_escape(dprec['MAC'])] | |
568 | ||
a4af0040 | 569 | self._vsctl_argv = vsctl_argv |
b3080599 | 570 | self._extra_ports = extra_ports |
da7198b4 | 571 | self._bridge_flows = bridge_flows |
b3080599 IC |
572 | |
573 | def bring_down_existing(self): | |
fe19e820 BP |
574 | # interface-reconfigure is never explicitly called to down a |
575 | # bond master. However, when we are called to up a slave it | |
f51c1d98 BP |
576 | # is implicit that we are destroying the master. Conversely, |
577 | # when we are called to up a bond is is implicit that we are | |
578 | # taking down the slaves. | |
fe19e820 | 579 | # |
f51c1d98 BP |
580 | # This is (only) important in the case where the device being |
581 | # implicitly taken down uses DHCP. We need to kill the | |
582 | # dhclient process, otherwise performing the inverse operation | |
583 | # later later will fail because ifup will refuse to start a | |
584 | # duplicate dhclient. | |
fe19e820 BP |
585 | bond_masters = pif_get_bond_masters(self._pif) |
586 | for master in bond_masters: | |
587 | log("action_up: bring down bond master %s" % (pif_netdev_name(master))) | |
588 | run_command(["/sbin/ifdown", pif_bridge_name(master)]) | |
b3080599 | 589 | |
f51c1d98 BP |
590 | bond_slaves = pif_get_bond_slaves(self._pif) |
591 | for slave in bond_slaves: | |
592 | log("action_up: bring down bond slave %s" % (pif_netdev_name(slave))) | |
593 | run_command(["/sbin/ifdown", pif_bridge_name(slave)]) | |
594 | ||
b3080599 IC |
595 | def configure(self): |
596 | # Bring up physical devices. ovs-vswitchd initially enables or | |
597 | # disables bond slaves based on whether carrier is detected | |
598 | # when they are added, and a network device that is down | |
599 | # always reports "no carrier". | |
600 | physical_devices = datapath_get_physical_pifs(self._dp) | |
c64540e3 EJ |
601 | |
602 | if pif_is_bond(self._dp): | |
603 | brec = db().get_pif_record(self._dp) | |
604 | bond_mtu = mtu_setting(brec['network'], "PIF", brec['other_config']) | |
605 | else: | |
606 | bond_mtu = None | |
b3080599 IC |
607 | |
608 | for p in physical_devices: | |
9a2b1175 IC |
609 | prec = db().get_pif_record(p) |
610 | oc = prec['other_config'] | |
b3080599 IC |
611 | |
612 | dev = pif_netdev_name(p) | |
613 | ||
c64540e3 EJ |
614 | if bond_mtu: |
615 | mtu = bond_mtu | |
616 | else: | |
617 | mtu = mtu_setting(prec['network'], "PIF", oc) | |
b3080599 IC |
618 | |
619 | netdev_up(dev, mtu) | |
620 | ||
404c1692 | 621 | settings, offload = ethtool_settings(oc, PIF_OTHERCONFIG_DEFAULTS) |
b3080599 IC |
622 | if len(settings): |
623 | run_command(['/sbin/ethtool', '-s', dev] + settings) | |
624 | if len(offload): | |
625 | run_command(['/sbin/ethtool', '-K', dev] + offload) | |
626 | ||
431488e6 BP |
627 | driver = netdev_get_driver_name(dev) |
628 | if 'vlan-bug-workaround' in oc: | |
629 | vlan_bug_workaround = oc['vlan-bug-workaround'] == 'true' | |
630 | elif driver in NO_VLAN_WORKAROUND_DRIVERS: | |
631 | vlan_bug_workaround = False | |
632 | else: | |
633 | vlan_bug_workaround = netdev_has_vlan_accel(dev) | |
634 | ||
635 | if vlan_bug_workaround: | |
636 | setting = 'on' | |
637 | else: | |
638 | setting = 'off' | |
639 | run_command(['/usr/sbin/ovs-vlan-bug-workaround', dev, setting]) | |
640 | ||
a4af0040 | 641 | datapath_modify_config(self._vsctl_argv) |
da7198b4 DT |
642 | if self._bridge_flows: |
643 | ofports = [] | |
644 | physical_devices = datapath_get_physical_pifs(self._dp) | |
645 | if len(physical_devices) > 1: | |
646 | for slave in physical_devices: | |
647 | name = pif_netdev_name(slave) | |
648 | ofport = vswitchCfgQuery(['get', 'interface', name, 'ofport']) | |
649 | ofports.append(ofport) | |
650 | else: | |
651 | name = pif_netdev_name(self._dp) | |
652 | ofport = vswitchCfgQuery(['get', 'interface', name, 'ofport']) | |
653 | ofports.append(ofport) | |
654 | dpname = pif_bridge_name(self._dp) | |
655 | for flow in self._bridge_flows: | |
656 | if flow.find('in_port=%s') != -1 or flow.find('actions=%s') != -1: | |
657 | for port in ofports: | |
1ea636ec | 658 | f = flow % (port.decode()) |
da7198b4 DT |
659 | run_command(['/usr/bin/ovs-ofctl', 'add-flow', dpname, f]) |
660 | else: | |
661 | run_command(['/usr/bin/ovs-ofctl', 'add-flow', dpname, flow]) | |
b3080599 IC |
662 | |
663 | def post(self): | |
664 | for p in self._extra_ports: | |
665 | log("action_up: bring up %s" % p) | |
666 | netdev_up(p) | |
667 | ||
668 | def bring_down(self): | |
a4af0040 | 669 | vsctl_argv = [] |
b3080599 IC |
670 | |
671 | dp = self._dp | |
672 | ipdev = self._ipdev | |
673 | ||
674 | bridge = pif_bridge_name(dp) | |
675 | ||
b3080599 | 676 | log("deconfigure ipdev %s on %s" % (ipdev,bridge)) |
a4af0040 JP |
677 | vsctl_argv += ["# deconfigure ipdev %s" % ipdev] |
678 | vsctl_argv += datapath_deconfigure_ipdev(ipdev) | |
b3080599 IC |
679 | |
680 | if pif_is_vlan(self._pif): | |
7966caf4 BP |
681 | # Delete the VLAN bridge. |
682 | vsctl_argv += deconfigure_bridge(self._pif) | |
683 | ||
b3080599 IC |
684 | # If the VLAN's slave is attached, leave datapath setup. |
685 | slave = pif_get_vlan_slave(self._pif) | |
686 | if db().get_pif_record(slave)['currently_attached']: | |
687 | log("action_down: vlan slave is currently attached") | |
688 | dp = None | |
689 | ||
690 | # If the VLAN's slave has other VLANs that are attached, leave datapath setup. | |
691 | for master in pif_get_vlan_masters(slave): | |
692 | if master != self._pif and db().get_pif_record(master)['currently_attached']: | |
693 | log("action_down: vlan slave has other master: %s" % pif_netdev_name(master)) | |
694 | dp = None | |
695 | ||
696 | # Otherwise, take down the datapath too (fall through) | |
697 | if dp: | |
698 | log("action_down: no more masters, bring down slave %s" % bridge) | |
699 | else: | |
700 | # Stop here if this PIF has attached VLAN masters. | |
701 | masters = [db().get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(self._pif) if db().get_pif_record(m)['currently_attached']] | |
702 | if len(masters) > 0: | |
703 | log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters)) | |
704 | dp = None | |
705 | ||
706 | if dp: | |
7966caf4 | 707 | vsctl_argv += deconfigure_bridge(dp) |
56cac225 | 708 | |
df9a459a IC |
709 | physical_devices = [pif_netdev_name(p) for p in datapath_get_physical_pifs(dp)] |
710 | ||
711 | log("action_down: bring down physical devices - %s" % physical_devices) | |
712 | ||
713 | for p in physical_devices: | |
714 | netdev_down(p) | |
715 | ||
56cac225 | 716 | datapath_modify_config(vsctl_argv) |
da7198b4 DT |
717 | |
718 | # | |
719 | # utility methods | |
720 | # | |
721 | ||
722 | def vswitchCfgQuery(action_args): | |
723 | cmd = ['%s/usr/bin/ovs-vsctl' % root_prefix(), | |
fba6bd1d | 724 | '-vconsole:off'] + action_args |
da7198b4 DT |
725 | output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate() |
726 | if len(output) == 0 or output[0] == None: | |
727 | output = "" | |
728 | else: | |
729 | output = output[0].strip() | |
730 | return output |