configure: Silence check for broken strtok_r().
[sliver-openvswitch.git] / xenserver / opt_xensource_libexec_InterfaceReconfigureVswitch.py
1 # Copyright (c) 2008,2009 Citrix Systems, Inc.
2 # Copyright (c) 2009 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
16 #
17 # Bare Network Devices -- network devices without IP configuration
18 #
19
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)
24         return
25     run_command(["/sbin/ifconfig", netdev, 'down'])
26
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)
31
32     if mtu:
33         mtu = ["mtu", mtu]
34     else:
35         mtu = []
36
37     run_command(["/sbin/ifconfig", netdev, 'up'] + mtu)
38
39 #
40 # Bridges
41 #
42
43 def pif_bridge_name(pif):
44     """Return the bridge name of a pif.
45
46     PIF must not be a VLAN and must be a bridged PIF."""
47
48     pifrec = db().get_pif_record(pif)
49
50     if pif_is_vlan(pif):
51         raise Error("PIF %(uuid)s cannot be a bridge, VLAN is %(VLAN)s" % pifrec)
52
53     nwrec = db().get_network_record(pifrec['network'])
54
55     if nwrec['bridge']:
56         return nwrec['bridge']
57     else:
58         raise Error("PIF %(uuid)s does not have a bridge name" % pifrec)
59
60 #
61 # PIF miscellanea
62 #
63
64 def pif_currently_in_use(pif):
65     """Determine if a PIF is currently in use.
66
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
71     """
72     rec = db().get_pif_record(pif)
73     if rec['currently_attached']:
74         log("configure_datapath: %s is currently attached" % (pif_netdev_name(pif)))
75         return True
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)))
79             return True
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)))
83             return True
84     return False
85
86 #
87 # Datapath Configuration
88 #
89
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.
94 """
95     if pif_is_vlan(pif):
96         return pif_datapath(pif_get_vlan_slave(pif))
97
98     pifrec = db().get_pif_record(pif)
99     nwrec = db().get_network_record(pifrec['network'])
100     if not nwrec['bridge']:
101         return None
102     else:
103         return pif
104
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.
109
110 A VLAN PIF cannot be a datapath PIF.
111 """
112     if pif_is_vlan(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)
117     else:
118         return [pif]
119
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
123     # interfaces.
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]
128
129 def datapath_configure_bond(pif,slaves):
130     pifrec = db().get_pif_record(pif)
131     interface = pif_netdev_name(pif)
132
133     argv = ['--del-match=bonding.%s.[!0-9]*' % interface]
134     argv += ["--add=bonding.%s.slave=%s" % (interface, pif_netdev_name(slave))
135              for slave in slaves]
136     argv += ['--add=bonding.%s.fake-iface=true' % interface]
137
138     if pifrec['MAC'] != "":
139         argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
140
141     # Bonding options.
142     bond_options = {
143         "mode":   "balance-slb",
144         "miimon": "100",
145         "downdelay": "200",
146         "updelay": "31000",
147         "use_carrier": "1",
148         }
149     # override defaults with values from other-config whose keys
150     # being with "bond-"
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)]
158     return argv
159
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
163     # interfaces.
164     return ['--del-match=bonding.%s.[!0-9]*' % netdev,
165             '--del-match=port.%s.[!0-9]*' % netdev]
166
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
170     # interfaces.
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]
176
177 def datapath_modify_config(commands):
178     #log("modifying configuration:")
179     #for c in commands:
180     #    log("  %s" % c)
181
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'])
185     if not rc:
186         raise Error("Failed to modify vswitch configuration")
187     run_command(['/sbin/service', 'vswitch', 'reload'])
188     return True
189
190 #
191 # Toplevel Datapath Configuration.
192 #
193
194 def configure_datapath(pif):
195     """Bring up the datapath configuration for PIF.
196
197     Should be careful not to glitch existing users of the datapath, e.g. other VLANs etc.
198
199     Should take care of tearing down other PIFs which encompass common physical devices.
200
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.
205     """
206
207     cfgmod_argv = []
208     extra_up_ports = []
209
210     bridge = pif_bridge_name(pif)
211
212     physical_devices = datapath_get_physical_pifs(pif)
213
214     # Determine additional devices to deconfigure.
215     #
216     # Given all physical devices which are part of this PIF we need to
217     # consider:
218     # - any additional bond which a physical device is part of.
219     # - any additional physical devices which are part of an additional bond.
220     #
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):
227             if bond == pif:
228                 log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond))
229                 continue
230             if bond in extra_down_bonds:
231                 continue
232             if db().get_pif_record(bond)['currently_attached']:
233                 log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond))
234
235             extra_down_bonds += [bond]
236
237             for s in pif_get_bond_slaves(bond):
238                 if s in physical_devices:
239                     continue
240                 if s in extra_down_ports:
241                     continue
242                 if pif_currently_in_use(s):
243                     continue
244                 extra_down_ports += [s]
245
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])
250
251     # Need to fully deconfigure any bridge which any of the:
252     # - physical devices
253     # - bond devices
254     # - sibling devices
255     # refers to
256     for brpif in physical_devices + extra_down_ports + extra_down_bonds:
257         if brpif == pif:
258             continue
259         b = pif_bridge_name(brpif)
260         #ifdown(b)
261         # XXX
262         netdev_down(b)
263         cfgmod_argv += ['# remove bridge %s' % b]
264         cfgmod_argv += ['--del-match=bridge.%s.*' % b]
265
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)
270         netdev_down(dev)
271
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)
276         netdev_down(dev)
277
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)]
290     else:
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) ]
294
295     return cfgmod_argv,extra_up_ports
296
297 def deconfigure_datapath(pif):
298     cfgmod_argv = []
299
300     bridge = pif_bridge_name(pif)
301
302     physical_devices = datapath_get_physical_pifs(pif)
303
304     log("deconfigure_datapath: bridge           - %s" % bridge)
305     log("deconfigure_datapath: physical devices - %s" % [pif_netdev_name(p) for p in physical_devices])
306
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)
311         netdev_down(dev)
312
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))
316
317     cfgmod_argv += ['# deconfigure bridge %s' % bridge]
318     cfgmod_argv += ['--del-match=bridge.%s.*' % bridge]
319
320     return cfgmod_argv
321
322 #
323 #
324 #
325
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)
331
332         if pif_is_vlan(pif) and not self._dp:
333             raise Error("Unbridged VLAN devices not implemented yet")
334         
335         log("Configured for Vswitch datapath")
336
337     def configure_ipdev(self, cfg):
338         cfg.write("TYPE=Ethernet\n")
339
340     def preconfigure(self, parent):
341         cfgmod_argv = []
342         extra_ports = []
343
344         pifrec = db().get_pif_record(self._pif)
345
346         ipdev = self._ipdev
347         bridge = pif_bridge_name(self._dp)
348         c,e = configure_datapath(self._dp)
349         cfgmod_argv += c
350         extra_ports += e
351
352         cfgmod_argv += ['# configure xs-network-uuids']
353         cfgmod_argv += ['--del-match=bridge.%s.xs-network-uuids=*' % bridge]
354
355         for nwpif in db().get_pifs_by_device(db().get_pif_record(self._pif)['device']):
356             rec = db().get_pif_record(nwpif)
357
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']))
363             #    continue
364             nwrec = db().get_network_record(rec['network'])
365             cfgmod_argv += ['--add=bridge.%s.xs-network-uuids=%s' % (bridge, nwrec['uuid'])]
366
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)]
371         if bridge == ipdev:
372             cfgmod_argv += ['--add=bridge.%s.mac=%s' % (bridge, pifrec['MAC'])]
373         else:
374             cfgmod_argv += ['--add=iface.%s.mac=%s' % (ipdev, pifrec['MAC'])]
375             
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)]
380
381         self._cfgmod_argv = cfgmod_argv
382         self._extra_ports = extra_ports
383
384     def bring_down_existing(self):
385         pass
386
387     def configure(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)
393         
394         for p in physical_devices:
395             oc = db().get_pif_record(p)['other_config']
396
397             dev = pif_netdev_name(p)
398
399             mtu = mtu_setting(oc)
400
401             netdev_up(dev, mtu)
402
403             settings, offload = ethtool_settings(oc)
404             if len(settings):
405                 run_command(['/sbin/ethtool', '-s', dev] + settings)
406             if len(offload):
407                 run_command(['/sbin/ethtool', '-K', dev] + offload)
408
409         datapath_modify_config(self._cfgmod_argv)
410
411     def post(self):
412         for p in self._extra_ports:
413             log("action_up: bring up %s" % p)
414             netdev_up(p)
415
416     def bring_down(self):
417         cfgmod_argv = []
418
419         dp = self._dp
420         ipdev = self._ipdev
421         
422         bridge = pif_bridge_name(dp)
423
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'])]
428
429         log("deconfigure ipdev %s on %s" % (ipdev,bridge))
430         cfgmod_argv += ["# deconfigure ipdev %s" % ipdev]
431         cfgmod_argv += datapath_deconfigure_ipdev(ipdev)
432
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")
438                 dp = None
439
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))
444                     dp = None
445
446             # Otherwise, take down the datapath too (fall through)
447             if dp:
448                 log("action_down: no more masters, bring down slave %s" % bridge)
449         else:
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']]
452             if len(masters) > 0:
453                 log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters))
454                 dp = None
455
456         if dp:
457             cfgmod_argv += deconfigure_datapath(dp)
458             datapath_modify_config(cfgmod_argv)