]> git.proxmox.com Git - mirror_ovs.git/blob - xenserver/opt_xensource_libexec_InterfaceReconfigureBridge.py
python: Remove unnecessary semicolons and slashes.
[mirror_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)