Merge branch 'master' into next
[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     return ['--', '--if-exists', 'del-port', netdev]
122
123 def datapath_configure_bond(pif,slaves):
124     bridge = pif_bridge_name(pif)
125     pifrec = db.get_pif_record(pif)
126     interface = pif_netdev_name(pif)
127
128     argv = ['--', '--fake-iface', 'add-bond', bridge, interface]
129     for slave in slaves:
130         argv += [pif_netdev_name(slave)]
131
132     # XXX need ovs-vsctl support
133     #if pifrec['MAC'] != "":
134     #    argv += ['--add=port.%s.mac=%s' % (interface, pifrec['MAC'])]
135
136     # Bonding options.
137     bond_options = {
138         "mode":   "balance-slb",
139         "miimon": "100",
140         "downdelay": "200",
141         "updelay": "31000",
142         "use_carrier": "1",
143         }
144     # override defaults with values from other-config whose keys
145     # being with "bond-"
146     oc = pifrec['other_config']
147     overrides = filter(lambda (key,val):
148                            key.startswith("bond-"), oc.items())
149     overrides = map(lambda (key,val): (key[5:], val), overrides)
150     bond_options.update(overrides)
151     for (name,val) in bond_options.items():
152         # XXX need ovs-vsctl support for bond options
153         #argv += ["--add=bonding.%s.%s=%s" % (interface, name, val)]
154         pass
155     return argv
156
157 def datapath_deconfigure_bond(netdev):
158     return ['--', '--if-exists', 'del-port', netdev]
159
160 def datapath_deconfigure_ipdev(interface):
161     return ['--', '--if-exists', 'del-port', interface]
162
163 def datapath_modify_config(commands):
164     if debug_mode():
165         log("modifying configuration:")
166         for c in commands:
167             log("  %s" % c)
168             
169     rc = run_command(['/usr/bin/ovs-vsctl'] + ['--timeout=20']
170                      + [c for c in commands if not c.startswith('#')])
171     if not rc:       
172         raise Error("Failed to modify vswitch configuration")
173     return True
174
175 #
176 # Toplevel Datapath Configuration.
177 #
178
179 def configure_datapath(pif):
180     """Bring up the datapath configuration for PIF.
181
182     Should be careful not to glitch existing users of the datapath, e.g. other VLANs etc.
183
184     Should take care of tearing down other PIFs which encompass common physical devices.
185
186     Returns a tuple containing
187     - A list containing the necessary vsctl command line arguments
188     - A list of additional devices which should be brought up after
189       the configuration is applied.
190     """
191
192     vsctl_argv = []
193     extra_up_ports = []
194
195     bridge = pif_bridge_name(pif)
196
197     physical_devices = datapath_get_physical_pifs(pif)
198
199     # Determine additional devices to deconfigure.
200     #
201     # Given all physical devices which are part of this PIF we need to
202     # consider:
203     # - any additional bond which a physical device is part of.
204     # - any additional physical devices which are part of an additional bond.
205     #
206     # Any of these which are not currently in use should be brought
207     # down and deconfigured.
208     extra_down_bonds = []
209     extra_down_ports = []
210     for p in physical_devices:
211         for bond in pif_get_bond_masters(p):
212             if bond == pif:
213                 log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond))
214                 continue
215             if bond in extra_down_bonds:
216                 continue
217             if db().get_pif_record(bond)['currently_attached']:
218                 log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond))
219
220             extra_down_bonds += [bond]
221
222             for s in pif_get_bond_slaves(bond):
223                 if s in physical_devices:
224                     continue
225                 if s in extra_down_ports:
226                     continue
227                 if pif_currently_in_use(s):
228                     continue
229                 extra_down_ports += [s]
230
231     log("configure_datapath: bridge      - %s" % bridge)
232     log("configure_datapath: physical    - %s" % [pif_netdev_name(p) for p in physical_devices])
233     log("configure_datapath: extra ports - %s" % [pif_netdev_name(p) for p in extra_down_ports])
234     log("configure_datapath: extra bonds - %s" % [pif_netdev_name(p) for p in extra_down_bonds])
235
236     # Need to fully deconfigure any bridge which any of the:
237     # - physical devices
238     # - bond devices
239     # - sibling devices
240     # refers to
241     for brpif in physical_devices + extra_down_ports + extra_down_bonds:
242         if brpif == pif:
243             continue
244         b = pif_bridge_name(brpif)
245         #ifdown(b)
246         # XXX
247         netdev_down(b)
248         vsctl_argv += ['# remove bridge %s' % b]
249         vsctl_argv += ['--', '--if-exists', 'del-br', b]
250
251     for n in extra_down_ports:
252         dev = pif_netdev_name(n)
253         vsctl_argv += ['# deconfigure sibling physical device %s' % dev]
254         vsctl_argv += datapath_deconfigure_physical(dev)
255         netdev_down(dev)
256
257     for n in extra_down_bonds:
258         dev = pif_netdev_name(n)
259         vsctl_argv += ['# deconfigure bond device %s' % dev]
260         vsctl_argv += datapath_deconfigure_bond(dev)
261         netdev_down(dev)
262
263     for p in physical_devices:
264         dev = pif_netdev_name(p)
265         vsctl_argv += ['# deconfigure physical port %s' % dev]
266         vsctl_argv += datapath_deconfigure_physical(dev)
267
268     if len(physical_devices) > 1:
269         vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
270         vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
271         vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)]
272         vsctl_argv += datapath_configure_bond(pif, physical_devices)
273         extra_up_ports += [pif_netdev_name(pif)]
274      else:
275         iface = pif_netdev_name(physical_devices[0])
276         vsctl_argv += ['# add physical device %s' % iface]
277         vsctl_argv += ['--', 'add-port', bridge, iface]
278
279     return vsctl_argv,extra_up_ports
280
281 def deconfigure_datapath(pif):
282     vsctl_argv = []
283
284     bridge = pif_bridge_name(pif)
285
286     physical_devices = datapath_get_physical_pifs(pif)
287
288     log("deconfigure_datapath: bridge           - %s" % bridge)
289     log("deconfigure_datapath: physical devices - %s" % [pif_netdev_name(p) for p in physical_devices])
290
291     for p in physical_devices:
292         dev = pif_netdev_name(p)
293         vsctl_argv += ['# deconfigure physical port %s' % dev]
294         vsctl_argv += datapath_deconfigure_physical(dev)
295         netdev_down(dev)
296
297     if len(physical_devices) > 1:
298         vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
299         vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
300
301     vsctl_argv += ['# deconfigure bridge %s' % bridge]
302     vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
303
304     return vsctl_argv
305
306 #
307 #
308 #
309
310 class DatapathVswitch(Datapath):
311     def __init__(self, pif):
312         Datapath.__init__(self, pif)
313         self._dp = pif_datapath(pif)
314         self._ipdev = pif_ipdev_name(pif)
315
316         if pif_is_vlan(pif) and not self._dp:
317             raise Error("Unbridged VLAN devices not implemented yet")
318         
319         log("Configured for Vswitch datapath")
320
321     def configure_ipdev(self, cfg):
322         cfg.write("TYPE=Ethernet\n")
323
324     def preconfigure(self, parent):
325         vsctl_argv = []
326         extra_ports = []
327
328         pifrec = db().get_pif_record(self._pif)
329
330         ipdev = self._ipdev
331         bridge = pif_bridge_name(self._dp)
332         c,e = configure_datapath(self._dp)
333         vsctl_argv += c
334         extra_ports += e
335
336         if pif_is_vlan(pif):
337             datapath = pif_datapath(pif)
338             vsctl_argv += ['--', 'add-br', bridge, datapath, pifrec['VLAN']]
339         else:
340             vsctl_argv += ['--', 'add-br', bridge]
341
342         xs_network_uuids = []
343         for nwpif in db().get_pifs_by_device(db().get_pif_record(self._pif)['device']):
344             rec = db().get_pif_record(nwpif)
345
346             # When state is read from dbcache PIF.currently_attached
347             # is always assumed to be false... Err on the side of
348             # listing even detached networks for the time being.
349             #if nwpif != pif and not rec['currently_attached']:
350             #    log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
351             #    continue
352             nwrec = db().get_network_record(rec['network'])
353             xs_network_uuids += [nwrec['uuid']]
354
355         vsctl_argv += ['# configure xs-network-uuids']
356         vsctl_argv += ['--', 'br-set-external-id', bridge,
357                 'xs-network-uuids', ';'.join(xs_network_uuids)]
358
359         vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
360         vsctl_argv += datapath_deconfigure_ipdev(ipdev)
361         vsctl_argv += ["# reconfigure ipdev %s" % ipdev]
362         vsctl_argv += ['--', 'add-port', bridge, ipdev]
363
364         # XXX Needs support in ovs-vsctl
365         #if bridge == ipdev:
366         #    vsctl_argv += ['--add=bridge.%s.mac=%s' % (bridge, pifrec['MAC'])]
367         #else:
368         #    vsctl_argv += ['--add=iface.%s.mac=%s' % (ipdev, pifrec['MAC'])]
369
370         self._vsctl_argv = vsctl_argv
371         self._extra_ports = extra_ports
372
373     def bring_down_existing(self):
374         pass
375
376     def configure(self):
377         # Bring up physical devices. ovs-vswitchd initially enables or
378         # disables bond slaves based on whether carrier is detected
379         # when they are added, and a network device that is down
380         # always reports "no carrier".
381         physical_devices = datapath_get_physical_pifs(self._dp)
382         
383         for p in physical_devices:
384             oc = db().get_pif_record(p)['other_config']
385
386             dev = pif_netdev_name(p)
387
388             mtu = mtu_setting(oc)
389
390             netdev_up(dev, mtu)
391
392             settings, offload = ethtool_settings(oc)
393             if len(settings):
394                 run_command(['/sbin/ethtool', '-s', dev] + settings)
395             if len(offload):
396                 run_command(['/sbin/ethtool', '-K', dev] + offload)
397
398         datapath_modify_config(self._vsctl_argv)
399
400     def post(self):
401         for p in self._extra_ports:
402             log("action_up: bring up %s" % p)
403             netdev_up(p)
404
405     def bring_down(self):
406         vsctl_argv = []
407
408         dp = self._dp
409         ipdev = self._ipdev
410         
411         bridge = pif_bridge_name(dp)
412
413         #nw = db().get_pif_record(self._pif)['network']
414         #nwrec = db().get_network_record(nw)
415         #vsctl_argv += ['# deconfigure xs-network-uuids']
416         #vsctl_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])]
417
418         log("deconfigure ipdev %s on %s" % (ipdev,bridge))
419         vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
420         vsctl_argv += datapath_deconfigure_ipdev(ipdev)
421
422         if pif_is_vlan(self._pif):
423             # If the VLAN's slave is attached, leave datapath setup.
424             slave = pif_get_vlan_slave(self._pif)
425             if db().get_pif_record(slave)['currently_attached']:
426                 log("action_down: vlan slave is currently attached")
427                 dp = None
428
429             # If the VLAN's slave has other VLANs that are attached, leave datapath setup.
430             for master in pif_get_vlan_masters(slave):
431                 if master != self._pif and db().get_pif_record(master)['currently_attached']:
432                     log("action_down: vlan slave has other master: %s" % pif_netdev_name(master))
433                     dp = None
434
435             # Otherwise, take down the datapath too (fall through)
436             if dp:
437                 log("action_down: no more masters, bring down slave %s" % bridge)
438         else:
439             # Stop here if this PIF has attached VLAN masters.
440             masters = [db().get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(self._pif) if db().get_pif_record(m)['currently_attached']]
441             if len(masters) > 0:
442                 log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters))
443                 dp = None
444
445         if dp:
446             vsctl_argv += deconfigure_datapath(dp)
447             datapath_modify_config(vsctl_argv)