netdev-dpdk: fix mbuf leaks
[cascardo/ovs.git] / xenserver / opt_xensource_libexec_InterfaceReconfigureBridge.py
1 # Copyright (c) 2008,2009 Citrix Systems, Inc.
2 #
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU Lesser General Public License as published
5 # by the Free Software Foundation; version 2.1 only. with the special
6 # exception on linking described in file LICENSE.
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 # GNU Lesser General Public License for more details.
12 #
13 from InterfaceReconfigure import *
14
15 import sys
16 import time
17
18 sysfs_bonding_masters = root_prefix() + "/sys/class/net/bonding_masters"
19
20 def open_pif_ifcfg(pif):
21     pifrec = db().get_pif_record(pif)
22
23     interface = pif_netdev_name(pif)
24     log("Configuring %s (%s)" % (interface, pifrec['MAC']))
25
26     f = ConfigurationFile("%s/etc/sysconfig/network-scripts/ifcfg-%s" % (root_prefix(), interface))
27
28     f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
29             (os.path.basename(f.path()), os.path.basename(sys.argv[0])))
30     f.write("XEMANAGED=yes\n")
31     f.write("DEVICE=%s\n" % interface)
32     f.write("ONBOOT=no\n")
33
34     return f
35
36 #
37 # Bare Network Devices -- network devices without IP configuration
38 #
39
40 def netdev_down(netdev):
41     """Bring down a bare network device"""
42     if not netdev_exists(netdev):
43         log("netdev: down: device %s does not exist, ignoring" % netdev)
44         return
45     run_command(["/sbin/ifdown", netdev])
46
47 def netdev_up(netdev, mtu=None):
48     """Bring up a bare network device"""
49     #if not netdev_exists(netdev):
50     #    raise Error("netdev: up: device %s does not exist" % netdev)
51
52     run_command(["/sbin/ifup", netdev])
53
54 #
55 # Bonding driver
56 #
57
58 def load_bonding_driver():
59     log("Loading bonding driver")
60     run_command(["/sbin/modprobe", "bonding"])
61     try:
62         # bond_device_exists() uses the contents of sysfs_bonding_masters to work out which devices
63         # have already been created.  Unfortunately the driver creates "bond0" automatically at
64         # modprobe init.  Get rid of this now or our accounting will go wrong.
65         f = open(sysfs_bonding_masters, "w")
66         f.write("-bond0")
67         f.close()
68     except IOError, e:
69         log("Failed to load bonding driver: %s" % e)
70
71 def bonding_driver_loaded():
72     lines = open(root_prefix() + "/proc/modules").read().split("\n")
73     modules = [line.split(" ")[0] for line in lines]
74     return "bonding" in modules
75
76 def bond_device_exists(name):
77     f = open(sysfs_bonding_masters, "r")
78     bonds = f.readline().split()
79     f.close()
80     return name in bonds
81
82 def __create_bond_device(name):
83
84     if not bonding_driver_loaded():
85         load_bonding_driver()
86
87     if bond_device_exists(name):
88         log("bond master %s already exists, not creating" % name)
89     else:
90         log("Creating bond master %s" % name)
91         try:
92             f = open(sysfs_bonding_masters, "w")
93             f.write("+" + name)
94             f.close()
95         except IOError, e:
96             log("Failed to create %s: %s" % (name, e))
97
98 def create_bond_device(pif):
99     """Ensures that a bond master device exists in the kernel."""
100
101     if not pif_is_bond(pif):
102         return
103
104     __create_bond_device(pif_netdev_name(pif))
105
106 def __destroy_bond_device(name):
107     if bond_device_exists(name):
108         retries = 10 # 10 * 0.5 seconds
109         while retries > 0:
110             retries = retries - 1
111             log("Destroying bond master %s (%d attempts remain)" % (name,retries))
112             try:
113                 f = open(sysfs_bonding_masters, "w")
114                 f.write("-" + name)
115                 f.close()
116                 retries = 0
117             except IOError, e:
118                 time.sleep(0.5)
119     else:
120         log("bond master %s does not exist, not destroying" % name)
121
122 def destroy_bond_device(pif):
123     """No, Mr. Bond, I expect you to die."""
124
125     pifrec = db().get_pif_record(pif)
126
127     if not pif_is_bond(pif):
128         return
129
130     # If the bonding module isn't loaded then do nothing.
131     if not os.access(sysfs_bonding_masters, os.F_OK):
132         return
133
134     name = pif_netdev_name(pif)
135
136     __destroy_bond_device(name)
137
138 #
139 # Bring Interface up/down.
140 #
141
142 def bring_down_interface(pif, destroy=False):
143     """Bring down the interface associated with PIF.
144
145     Brings down the given interface as well as any physical interfaces
146     which are bond slaves of this one. This is because they will be
147     required when the bond is brought up."""
148
149     def destroy_bridge(pif):
150         """Bring down the bridge associated with a PIF."""
151         #if not pif_is_bridged(pif):
152         #    return
153         bridge = pif_bridge_name(pif)
154         if not netdev_exists(bridge):
155             log("destroy_bridge: bridge %s does not exist, ignoring" % bridge)
156             return
157         log("Destroy bridge %s" % bridge)
158         netdev_down(bridge)
159         run_command(["/usr/sbin/brctl", "delbr", bridge])
160
161     def destroy_vlan(pif):
162         vlan = pif_netdev_name(pif)
163         if not netdev_exists(vlan):
164             log("vconfig del: vlan %s does not exist, ignoring" % vlan)
165             return
166         log("Destroy vlan device %s" % vlan)
167         run_command(["/sbin/vconfig", "rem", vlan])
168
169     if pif_is_vlan(pif):
170         interface = pif_netdev_name(pif)
171         log("bring_down_interface: %s is a VLAN" % interface)
172         netdev_down(interface)
173
174         if destroy:
175             destroy_vlan(pif)
176             destroy_bridge(pif)
177         else:
178             return
179
180         slave = pif_get_vlan_slave(pif)
181         if db().get_pif_record(slave)['currently_attached']:
182             log("bring_down_interface: vlan slave is currently attached")
183             return
184
185         masters = pif_get_vlan_masters(slave)
186         masters = [m for m in masters if m != pif and db().get_pif_record(m)['currently_attached']]
187         if len(masters) > 0:
188             log("bring_down_interface: vlan slave has other masters")
189             return
190
191         log("bring_down_interface: no more masters, bring down vlan slave %s" % pif_netdev_name(slave))
192         pif = slave
193     else:
194         vlan_masters = pif_get_vlan_masters(pif)
195         log("vlan masters of %s - %s" % (db().get_pif_record(pif)['device'], [pif_netdev_name(m) for m in vlan_masters]))
196         if len([m for m in vlan_masters if db().get_pif_record(m)['currently_attached']]) > 0:
197             log("Leaving %s up due to currently attached VLAN masters" % pif_netdev_name(pif))
198             return
199
200     # pif is now either a bond or a physical device which needs to be brought down
201
202     # Need to bring down bond slaves first since the bond device
203     # must be up to enslave/unenslave.
204     bond_slaves = pif_get_bond_slaves_sorted(pif)
205     log("bond slaves of %s - %s" % (db().get_pif_record(pif)['device'], [pif_netdev_name(s) for s in bond_slaves]))
206     for slave in bond_slaves:
207         slave_interface = pif_netdev_name(slave)
208         if db().get_pif_record(slave)['currently_attached']:
209             log("leave bond slave %s up (currently attached)" % slave_interface)
210             continue
211         log("bring down bond slave %s" % slave_interface)
212         netdev_down(slave_interface)
213         # Also destroy the bridge associated with the slave, since
214         # it will carry the MAC address and possibly an IP address
215         # leading to confusion.
216         destroy_bridge(slave)
217
218     interface = pif_netdev_name(pif)
219     log("Bring interface %s down" % interface)
220     netdev_down(interface)
221
222     if destroy:
223         destroy_bond_device(pif)
224         destroy_bridge(pif)
225
226 def interface_is_up(pif):
227     try:
228         interface = pif_netdev_name(pif)
229         state = open("%s/sys/class/net/%s/operstate" % (root_prefix(), interface)).read().strip()
230         return state == "up"
231     except:
232         return False # interface prolly doesn't exist
233
234 def bring_up_interface(pif):
235     """Bring up the interface associated with a PIF.
236
237     Also bring up the interfaces listed in additional.
238     """
239
240     # VLAN on bond seems to need bond brought up explicitly, but VLAN
241     # on normal device does not. Might as well always bring it up.
242     if pif_is_vlan(pif):
243         slave = pif_get_vlan_slave(pif)
244         if not interface_is_up(slave):
245             bring_up_interface(slave)
246
247     interface = pif_netdev_name(pif)
248
249     create_bond_device(pif)
250
251     log("Bring interface %s up" % interface)
252     netdev_up(interface)
253
254
255 #
256 # Datapath topology configuration.
257 #
258
259 def _configure_physical_interface(pif):
260     """Write the configuration for a physical interface.
261
262     Writes the configuration file for the physical interface described by
263     the pif object.
264
265     Returns the open file handle for the interface configuration file.
266     """
267
268     pifrec = db().get_pif_record(pif)
269
270     log("Configuring physical interface %s" % pifrec['device'])
271
272     f = open_pif_ifcfg(pif)
273
274     f.write("TYPE=Ethernet\n")
275     f.write("HWADDR=%(MAC)s\n" % pifrec)
276
277     settings,offload = ethtool_settings(pifrec['other_config'],
278                                         PIF_OTHERCONFIG_DEFAULTS)
279     if len(settings):
280         f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings))
281     if len(offload):
282         f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload))
283
284     mtu = mtu_setting(pifrec['network'], "PIF", pifrec['other_config'])
285     if mtu:
286         f.write("MTU=%s\n" % mtu)
287
288     return f
289
290 def pif_get_bond_slaves_sorted(pif):
291     pifrec = db().get_pif_record(pif)
292
293     # build a list of slave's pifs
294     slave_pifs = pif_get_bond_slaves(pif)
295
296     # Ensure any currently attached slaves are listed in the opposite order to the order in
297     # which they were attached.  The first slave attached must be the last detached since
298     # the bond is using its MAC address.
299     try:
300         attached_slaves = open("%s/sys/class/net/%s/bonding/slaves" % (root_prefix(), pifrec['device'])).readline().split()
301         for slave in attached_slaves:
302             pifs = [p for p in db().get_pifs_by_device(slave) if not pif_is_vlan(p)]
303             slave_pif = pifs[0]
304             slave_pifs.remove(slave_pif)
305             slave_pifs.insert(0, slave_pif)
306     except IOError:
307         pass
308
309     return slave_pifs
310
311 def _configure_bond_interface(pif):
312     """Write the configuration for a bond interface.
313
314     Writes the configuration file for the bond interface described by
315     the pif object. Handles writing the configuration for the slave
316     interfaces.
317
318     Returns the open file handle for the bond interface configuration
319     file.
320     """
321
322     pifrec = db().get_pif_record(pif)
323
324     f = open_pif_ifcfg(pif)
325
326     if pifrec['MAC'] != "":
327         f.write("MACADDR=%s\n" % pifrec['MAC'])
328
329     for slave in pif_get_bond_slaves(pif):
330         s = _configure_physical_interface(slave)
331         s.write("MASTER=%(device)s\n" % pifrec)
332         s.write("SLAVE=yes\n")
333         s.close()
334         f.attach_child(s)
335
336     settings,offload = ethtool_settings(pifrec['other_config'])
337     if len(settings):
338         f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings))
339     if len(offload):
340         f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload))
341
342     mtu = mtu_setting(pifrec['network'], "Bond-PIF", pifrec['other_config'])
343     if mtu:
344         f.write("MTU=%s\n" % mtu)
345
346     # The bond option defaults
347     bond_options = {
348         "mode":   "balance-slb",
349         "miimon": "100",
350         "downdelay": "200",
351         "updelay": "31000",
352         "use_carrier": "1",
353         "hashing-algorithm": "src_mac",
354         }
355
356     # override defaults with values from other-config whose keys being with "bond-"
357     oc = pifrec['other_config']
358     overrides = filter(lambda (key,val): key.startswith("bond-"), oc.items())
359     overrides = map(lambda (key,val): (key[5:], val), overrides)
360     bond_options.update(overrides)
361
362     # write the bond options to ifcfg-bondX
363     f.write('BONDING_OPTS="')
364     for (name,val) in bond_options.items():
365         f.write("%s=%s " % (name,val))
366     f.write('"\n')
367     return f
368
369 def _configure_vlan_interface(pif):
370     """Write the configuration for a VLAN interface.
371
372     Writes the configuration file for the VLAN interface described by
373     the pif object. Handles writing the configuration for the master
374     interface if necessary.
375
376     Returns the open file handle for the VLAN interface configuration
377     file.
378     """
379
380     slave = _configure_pif(pif_get_vlan_slave(pif))
381
382     pifrec = db().get_pif_record(pif)
383
384     f = open_pif_ifcfg(pif)
385     f.write("VLAN=yes\n")
386
387     settings,offload = ethtool_settings(pifrec['other_config'])
388     if len(settings):
389         f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings))
390     if len(offload):
391         f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload))
392
393     mtu = mtu_setting(pifrec['network'], "VLAN-PIF", pifrec['other_config'])
394     if mtu:
395         f.write("MTU=%s\n" % mtu)
396
397     f.attach_child(slave)
398
399     return f
400
401 def _configure_pif(pif):
402     """Write the configuration for a PIF object.
403
404     Writes the configuration file the PIF and all dependent
405     interfaces (bond slaves and VLAN masters etc).
406
407     Returns the open file handle for the interface configuration file.
408     """
409
410     if pif_is_vlan(pif):
411         f = _configure_vlan_interface(pif)
412     elif pif_is_bond(pif):
413         f = _configure_bond_interface(pif)
414     else:
415         f = _configure_physical_interface(pif)
416
417     f.write("BRIDGE=%s\n" % pif_bridge_name(pif))
418     f.close()
419
420     return f
421
422 #
423 #
424 #
425
426 class DatapathBridge(Datapath):
427     def __init__(self, pif):
428         if pif_is_tunnel(pif):
429             raise Error("Tunnel PIFs are not supported in Bridge mode")
430
431         Datapath.__init__(self, pif)
432         log("Configured for Bridge datapath")
433
434     def configure_ipdev(self, cfg):
435         if pif_is_bridged(self._pif):
436             cfg.write("TYPE=Bridge\n")
437             cfg.write("DELAY=0\n")
438             cfg.write("STP=off\n")
439             cfg.write("PIFDEV=%s\n" % pif_netdev_name(self._pif))
440         else:
441             cfg.write("TYPE=Ethernet\n")
442         
443     def preconfigure(self, parent):
444         pf = _configure_pif(self._pif)
445         parent.attach_child(pf)
446
447     def bring_down_existing(self):
448         # Bring down any VLAN masters so that we can reconfigure the slave.
449         for master in pif_get_vlan_masters(self._pif):
450             name = pif_netdev_name(master)
451             log("action_up: bring down vlan master %s" % (name))
452             netdev_down(name)
453
454         # interface-reconfigure is never explicitly called to down a bond master.
455         # However, when we are called to up a slave it is implicit that we are destroying the master.
456         bond_masters = pif_get_bond_masters(self._pif)
457         for master in bond_masters:
458             log("action_up: bring down bond master %s" % (pif_netdev_name(master)))
459             # bring down master
460             bring_down_interface(master, destroy=True)
461
462         # No masters left - now its safe to reconfigure the slave.
463         bring_down_interface(self._pif)
464         
465     def configure(self):
466         bring_up_interface(self._pif)
467
468     def post(self):
469         # Bring back any currently-attached VLAN masters
470         for master in [v for v in pif_get_vlan_masters(self._pif) if db().get_pif_record(v)['currently_attached']]:
471             name = pif_netdev_name(master)
472             log("action_up: bring up %s" % (name))
473             netdev_up(name)
474
475     def bring_down(self):
476         bring_down_interface(self._pif, destroy=True)