Rename UNUSED macro to OVS_UNUSED to avoid naming conflict.
[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         dprec = db().get_pif_record(self._dp)
346
347         ipdev = self._ipdev
348         bridge = pif_bridge_name(self._dp)
349         c,e = configure_datapath(self._dp)
350         cfgmod_argv += c
351         extra_ports += e
352
353         cfgmod_argv += ['# configure xs-network-uuids']
354         cfgmod_argv += ['--del-match=bridge.%s.xs-network-uuids=*' % bridge]
355
356         for nwpif in db().get_pifs_by_device(db().get_pif_record(self._pif)['device']):
357             rec = db().get_pif_record(nwpif)
358
359             # When state is read from dbcache PIF.currently_attached
360             # is always assumed to be false... Err on the side of
361             # listing even detached networks for the time being.
362             #if nwpif != pif and not rec['currently_attached']:
363             #    log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
364             #    continue
365             nwrec = db().get_network_record(rec['network'])
366             cfgmod_argv += ['--add=bridge.%s.xs-network-uuids=%s' % (bridge, nwrec['uuid'])]
367
368         cfgmod_argv += ["# deconfigure ipdev %s" % ipdev]
369         cfgmod_argv += datapath_deconfigure_ipdev(ipdev)
370         cfgmod_argv += ["# reconfigure ipdev %s" % ipdev]
371         cfgmod_argv += ['--add=bridge.%s.port=%s' % (bridge, ipdev)]
372         if bridge == ipdev:
373             cfgmod_argv += ['--add=bridge.%s.mac=%s' % (bridge, dprec['MAC'])]
374         else:
375             cfgmod_argv += ['--add=iface.%s.mac=%s' % (ipdev, dprec['MAC'])]
376             
377         if pif_is_vlan(self._pif):
378             cfgmod_argv += ['--add=vlan.%s.tag=%s' % (ipdev, pifrec['VLAN'])]
379             cfgmod_argv += ['--add=iface.%s.internal=true' % (ipdev)]
380             cfgmod_argv += ['--add=iface.%s.fake-bridge=true' % (ipdev)]
381
382         self._cfgmod_argv = cfgmod_argv
383         self._extra_ports = extra_ports
384
385     def bring_down_existing(self):
386         pass
387
388     def configure(self):
389         # Bring up physical devices. ovs-vswitchd initially enables or
390         # disables bond slaves based on whether carrier is detected
391         # when they are added, and a network device that is down
392         # always reports "no carrier".
393         physical_devices = datapath_get_physical_pifs(self._dp)
394         
395         for p in physical_devices:
396             oc = db().get_pif_record(p)['other_config']
397
398             dev = pif_netdev_name(p)
399
400             mtu = mtu_setting(oc)
401
402             netdev_up(dev, mtu)
403
404             settings, offload = ethtool_settings(oc)
405             if len(settings):
406                 run_command(['/sbin/ethtool', '-s', dev] + settings)
407             if len(offload):
408                 run_command(['/sbin/ethtool', '-K', dev] + offload)
409
410         datapath_modify_config(self._cfgmod_argv)
411
412     def post(self):
413         for p in self._extra_ports:
414             log("action_up: bring up %s" % p)
415             netdev_up(p)
416
417     def bring_down(self):
418         cfgmod_argv = []
419
420         dp = self._dp
421         ipdev = self._ipdev
422         
423         bridge = pif_bridge_name(dp)
424
425         #nw = db().get_pif_record(self._pif)['network']
426         #nwrec = db().get_network_record(nw)
427         #cfgmod_argv += ['# deconfigure xs-network-uuids']
428         #cfgmod_argv += ['--del-entry=bridge.%s.xs-network-uuids=%s' % (bridge,nwrec['uuid'])]
429
430         log("deconfigure ipdev %s on %s" % (ipdev,bridge))
431         cfgmod_argv += ["# deconfigure ipdev %s" % ipdev]
432         cfgmod_argv += datapath_deconfigure_ipdev(ipdev)
433
434         if pif_is_vlan(self._pif):
435             # If the VLAN's slave is attached, leave datapath setup.
436             slave = pif_get_vlan_slave(self._pif)
437             if db().get_pif_record(slave)['currently_attached']:
438                 log("action_down: vlan slave is currently attached")
439                 dp = None
440
441             # If the VLAN's slave has other VLANs that are attached, leave datapath setup.
442             for master in pif_get_vlan_masters(slave):
443                 if master != self._pif and db().get_pif_record(master)['currently_attached']:
444                     log("action_down: vlan slave has other master: %s" % pif_netdev_name(master))
445                     dp = None
446
447             # Otherwise, take down the datapath too (fall through)
448             if dp:
449                 log("action_down: no more masters, bring down slave %s" % bridge)
450         else:
451             # Stop here if this PIF has attached VLAN masters.
452             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             if len(masters) > 0:
454                 log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters))
455                 dp = None
456
457         if dp:
458             cfgmod_argv += deconfigure_datapath(dp)
459             datapath_modify_config(cfgmod_argv)