]> git.proxmox.com Git - ovs.git/blob - xenserver/opt_xensource_libexec_InterfaceReconfigureBridge.py
Merge "sflow" into "master".
[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 = "/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("/etc/sysconfig/network-scripts/ifcfg-%s" % 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("/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 # Bridges
140 #
141
142 def pif_is_bridged(pif):
143 pifrec = db().get_pif_record(pif)
144 nwrec = db().get_network_record(pifrec['network'])
145
146 if nwrec['bridge']:
147 # TODO: sanity check that nwrec['bridgeless'] != 'true'
148 return True
149 else:
150 # TODO: sanity check that nwrec['bridgeless'] == 'true'
151 return False
152
153 def pif_bridge_name(pif):
154 """Return the bridge name of a pif.
155
156 PIF must be a bridged PIF."""
157 pifrec = db().get_pif_record(pif)
158
159 nwrec = db().get_network_record(pifrec['network'])
160
161 if nwrec['bridge']:
162 return nwrec['bridge']
163 else:
164 raise Error("PIF %(uuid)s does not have a bridge name" % pifrec)
165
166 #
167 # Bring Interface up/down.
168 #
169
170 def bring_down_interface(pif, destroy=False):
171 """Bring down the interface associated with PIF.
172
173 Brings down the given interface as well as any physical interfaces
174 which are bond slaves of this one. This is because they will be
175 required when the bond is brought up."""
176
177 def destroy_bridge(pif):
178 """Bring down the bridge associated with a PIF."""
179 #if not pif_is_bridged(pif):
180 # return
181 bridge = pif_bridge_name(pif)
182 if not netdev_exists(bridge):
183 log("destroy_bridge: bridge %s does not exist, ignoring" % bridge)
184 return
185 log("Destroy bridge %s" % bridge)
186 netdev_down(bridge)
187 run_command(["/usr/sbin/brctl", "delbr", bridge])
188
189 def destroy_vlan(pif):
190 vlan = pif_netdev_name(pif)
191 if not netdev_exists(vlan):
192 log("vconfig del: vlan %s does not exist, ignoring" % vlan)
193 return
194 log("Destroy vlan device %s" % vlan)
195 run_command(["/sbin/vconfig", "rem", vlan])
196
197 if pif_is_vlan(pif):
198 interface = pif_netdev_name(pif)
199 log("bring_down_interface: %s is a VLAN" % interface)
200 netdev_down(interface)
201
202 if destroy:
203 destroy_vlan(pif)
204 destroy_bridge(pif)
205 else:
206 return
207
208 slave = pif_get_vlan_slave(pif)
209 if db().get_pif_record(slave)['currently_attached']:
210 log("bring_down_interface: vlan slave is currently attached")
211 return
212
213 masters = pif_get_vlan_masters(slave)
214 masters = [m for m in masters if m != pif and db().get_pif_record(m)['currently_attached']]
215 if len(masters) > 0:
216 log("bring_down_interface: vlan slave has other masters")
217 return
218
219 log("bring_down_interface: no more masters, bring down vlan slave %s" % pif_netdev_name(slave))
220 pif = slave
221 else:
222 vlan_masters = pif_get_vlan_masters(pif)
223 log("vlan masters of %s - %s" % (db().get_pif_record(pif)['device'], [pif_netdev_name(m) for m in vlan_masters]))
224 if len([m for m in vlan_masters if db().get_pif_record(m)['currently_attached']]) > 0:
225 log("Leaving %s up due to currently attached VLAN masters" % pif_netdev_name(pif))
226 return
227
228 # pif is now either a bond or a physical device which needs to be brought down
229
230 # Need to bring down bond slaves first since the bond device
231 # must be up to enslave/unenslave.
232 bond_slaves = pif_get_bond_slaves_sorted(pif)
233 log("bond slaves of %s - %s" % (db().get_pif_record(pif)['device'], [pif_netdev_name(s) for s in bond_slaves]))
234 for slave in bond_slaves:
235 slave_interface = pif_netdev_name(slave)
236 if db().get_pif_record(slave)['currently_attached']:
237 log("leave bond slave %s up (currently attached)" % slave_interface)
238 continue
239 log("bring down bond slave %s" % slave_interface)
240 netdev_down(slave_interface)
241 # Also destroy the bridge associated with the slave, since
242 # it will carry the MAC address and possibly an IP address
243 # leading to confusion.
244 destroy_bridge(slave)
245
246 interface = pif_netdev_name(pif)
247 log("Bring interface %s down" % interface)
248 netdev_down(interface)
249
250 if destroy:
251 destroy_bond_device(pif)
252 destroy_bridge(pif)
253
254 def interface_is_up(pif):
255 try:
256 interface = pif_netdev_name(pif)
257 state = open("/sys/class/net/%s/operstate" % interface).read().strip()
258 return state == "up"
259 except:
260 return False # interface prolly doesn't exist
261
262 def bring_up_interface(pif):
263 """Bring up the interface associated with a PIF.
264
265 Also bring up the interfaces listed in additional.
266 """
267
268 # VLAN on bond seems to need bond brought up explicitly, but VLAN
269 # on normal device does not. Might as well always bring it up.
270 if pif_is_vlan(pif):
271 slave = pif_get_vlan_slave(pif)
272 if not interface_is_up(slave):
273 bring_up_interface(slave)
274
275 interface = pif_netdev_name(pif)
276
277 create_bond_device(pif)
278
279 log("Bring interface %s up" % interface)
280 netdev_up(interface)
281
282
283 #
284 # Datapath topology configuration.
285 #
286
287 def _configure_physical_interface(pif):
288 """Write the configuration for a physical interface.
289
290 Writes the configuration file for the physical interface described by
291 the pif object.
292
293 Returns the open file handle for the interface configuration file.
294 """
295
296 pifrec = db().get_pif_record(pif)
297
298 f = open_pif_ifcfg(pif)
299
300 f.write("TYPE=Ethernet\n")
301 f.write("HWADDR=%(MAC)s\n" % pifrec)
302
303 settings,offload = ethtool_settings(pifrec['other_config'])
304 if len(settings):
305 f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings))
306 if len(offload):
307 f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload))
308
309 mtu = mtu_setting(pifrec['other_config'])
310 if mtu:
311 f.write("MTU=%s\n" % mtu)
312
313 return f
314
315 def pif_get_bond_slaves_sorted(pif):
316 pifrec = db().get_pif_record(pif)
317
318 # build a list of slave's pifs
319 slave_pifs = pif_get_bond_slaves(pif)
320
321 # Ensure any currently attached slaves are listed in the opposite order to the order in
322 # which they were attached. The first slave attached must be the last detached since
323 # the bond is using its MAC address.
324 try:
325 attached_slaves = open("/sys/class/net/%s/bonding/slaves" % pifrec['device']).readline().split()
326 for slave in attached_slaves:
327 pifs = [p for p in db().get_pifs_by_device(slave) if not pif_is_vlan(p)]
328 slave_pif = pifs[0]
329 slave_pifs.remove(slave_pif)
330 slave_pifs.insert(0, slave_pif)
331 except IOError:
332 pass
333
334 return slave_pifs
335
336 def _configure_bond_interface(pif):
337 """Write the configuration for a bond interface.
338
339 Writes the configuration file for the bond interface described by
340 the pif object. Handles writing the configuration for the slave
341 interfaces.
342
343 Returns the open file handle for the bond interface configuration
344 file.
345 """
346
347 pifrec = db().get_pif_record(pif)
348
349 f = open_pif_ifcfg(pif)
350
351 if pifrec['MAC'] != "":
352 f.write("MACADDR=%s\n" % pifrec['MAC'])
353
354 for slave in pif_get_bond_slaves(pif):
355 s = _configure_physical_interface(slave)
356 s.write("MASTER=%(device)s\n" % pifrec)
357 s.write("SLAVE=yes\n")
358 s.close()
359 f.attach_child(s)
360
361 settings,offload = ethtool_settings(pifrec['other_config'])
362 if len(settings):
363 f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings))
364 if len(offload):
365 f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload))
366
367 mtu = mtu_setting(pifrec['other_config'])
368 if mtu:
369 f.write("MTU=%s\n" % mtu)
370
371 # The bond option defaults
372 bond_options = {
373 "mode": "balance-slb",
374 "miimon": "100",
375 "downdelay": "200",
376 "updelay": "31000",
377 "use_carrier": "1",
378 }
379
380 # override defaults with values from other-config whose keys being with "bond-"
381 oc = pifrec['other_config']
382 overrides = filter(lambda (key,val): key.startswith("bond-"), oc.items())
383 overrides = map(lambda (key,val): (key[5:], val), overrides)
384 bond_options.update(overrides)
385
386 # write the bond options to ifcfg-bondX
387 f.write('BONDING_OPTS="')
388 for (name,val) in bond_options.items():
389 f.write("%s=%s " % (name,val))
390 f.write('"\n')
391 return f
392
393 def _configure_vlan_interface(pif):
394 """Write the configuration for a VLAN interface.
395
396 Writes the configuration file for the VLAN interface described by
397 the pif object. Handles writing the configuration for the master
398 interface if necessary.
399
400 Returns the open file handle for the VLAN interface configuration
401 file.
402 """
403
404 slave = _configure_pif(pif_get_vlan_slave(pif))
405
406 pifrec = db().get_pif_record(pif)
407
408 f = open_pif_ifcfg(pif)
409 f.write("VLAN=yes\n")
410
411 settings,offload = ethtool_settings(pifrec['other_config'])
412 if len(settings):
413 f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings))
414 if len(offload):
415 f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload))
416
417 mtu = mtu_setting(pifrec['other_config'])
418 if mtu:
419 f.write("MTU=%s\n" % mtu)
420
421 f.attach_child(slave)
422
423 return f
424
425 def _configure_pif(pif):
426 """Write the configuration for a PIF object.
427
428 Writes the configuration file the PIF and all dependent
429 interfaces (bond slaves and VLAN masters etc).
430
431 Returns the open file handle for the interface configuration file.
432 """
433
434 if pif_is_vlan(pif):
435 f = _configure_vlan_interface(pif)
436 elif pif_is_bond(pif):
437 f = _configure_bond_interface(pif)
438 else:
439 f = _configure_physical_interface(pif)
440
441 f.write("BRIDGE=%s\n" % pif_bridge_name(pif))
442 f.close()
443
444 return f
445
446 #
447 #
448 #
449
450 class DatapathBridge(Datapath):
451 def __init__(self, pif):
452 Datapath.__init__(self, pif)
453 log("Configured for Bridge datapath")
454
455 def configure_ipdev(self, cfg):
456 if pif_is_bridged(self._pif):
457 cfg.write("TYPE=Bridge\n")
458 cfg.write("DELAY=0\n")
459 cfg.write("STP=off\n")
460 cfg.write("PIFDEV=%s\n" % pif_netdev_name(self._pif))
461 else:
462 cfg.write("TYPE=Ethernet\n")
463
464 def preconfigure(self, parent):
465 pf = _configure_pif(self._pif)
466 parent.attach_child(pf)
467
468 def bring_down_existing(self):
469 # Bring down any VLAN masters so that we can reconfigure the slave.
470 for master in pif_get_vlan_masters(self._pif):
471 name = pif_netdev_name(master)
472 log("action_up: bring down vlan master %s" % (name))
473 netdev_down(name)
474
475 # interface-reconfigure is never explicitly called to down a bond master.
476 # However, when we are called to up a slave it is implicit that we are destroying the master.
477 bond_masters = pif_get_bond_masters(self._pif)
478 for master in bond_masters:
479 log("action_up: bring down bond master %s" % (pif_netdev_name(master)))
480 # bring down master
481 bring_down_interface(master, destroy=True)
482
483 # No masters left - now its safe to reconfigure the slave.
484 bring_down_interface(self._pif)
485
486 def configure(self):
487 bring_up_interface(self._pif)
488
489 def post(self):
490 # Bring back any currently-attached VLAN masters
491 for master in [v for v in pif_get_vlan_masters(self._pif) if db().get_pif_record(v)['currently_attached']]:
492 name = pif_netdev_name(master)
493 log("action_up: bring up %s" % (name))
494 netdev_up(name)
495
496 def bring_down(self):
497 bring_down_interface(self._pif, destroy=True)