]>
Commit | Line | Data |
---|---|---|
8a77bb2e | 1 | #!/usr/bin/env python |
064af421 | 2 | # |
c4b1912e | 3 | # Copyright (c) 2008,2009 Citrix Systems, Inc. |
064af421 | 4 | # |
c4b1912e IC |
5 | # This program is free software; you can redistribute it and/or modify |
6 | # it under the terms of the GNU Lesser General Public License as published | |
7 | # by the Free Software Foundation; version 2.1 only. with the special | |
8 | # exception on linking described in file LICENSE. | |
9 | # | |
10 | # This program is distributed in the hope that it will be useful, | |
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | # GNU Lesser General Public License for more details. | |
14 | # | |
064af421 BP |
15 | """Usage: |
16 | ||
057fed2b BP |
17 | %(command-name)s <PIF> up |
18 | %(command-name)s <PIF> down | |
b3080599 | 19 | %(command-name)s rewrite |
057fed2b BP |
20 | %(command-name)s --force <BRIDGE> up |
21 | %(command-name)s --force <BRIDGE> down | |
b3080599 | 22 | %(command-name)s --force <BRIDGE> rewrite --device=<INTERFACE> --mac=<MAC-ADDRESS> <CONFIG> |
064af421 | 23 | |
057fed2b BP |
24 | where <PIF> is one of: |
25 | --session <SESSION-REF> --pif <PIF-REF> | |
26 | --pif-uuid <PIF-UUID> | |
27 | and <CONFIG> is one of: | |
28 | --mode=dhcp | |
29 | --mode=static --ip=<IPADDR> --netmask=<NM> [--gateway=<GW>] | |
064af421 BP |
30 | |
31 | Options: | |
b3080599 | 32 | --session A session reference to use to access the xapi DB |
057fed2b BP |
33 | --pif A PIF reference within the session. |
34 | --pif-uuid The UUID of a PIF. | |
35 | --force An interface name. | |
64ddb6fe | 36 | --root-prefix=DIR Use DIR as alternate root directory (for testing). |
b63fadcf | 37 | --no-syslog Write log messages to stderr instead of system log. |
064af421 BP |
38 | """ |
39 | ||
064af421 BP |
40 | # Notes: |
41 | # 1. Every pif belongs to exactly one network | |
42 | # 2. Every network has zero or one pifs | |
43 | # 3. A network may have an associated bridge, allowing vifs to be attached | |
44 | # 4. A network may be bridgeless (there's no point having a bridge over a storage pif) | |
45 | ||
b3080599 IC |
46 | from InterfaceReconfigure import * |
47 | ||
48 | import os, sys, getopt | |
064af421 BP |
49 | import syslog |
50 | import traceback | |
064af421 | 51 | import re |
2bb451b6 | 52 | import random |
acfb4e95 | 53 | import syslog |
064af421 | 54 | |
064af421 BP |
55 | management_pif = None |
56 | ||
1fa5a105 | 57 | dbcache_file = "/var/xapi/network.dbcache" |
064af421 | 58 | |
0b8870d4 | 59 | # |
b3080599 | 60 | # Logging. |
0b8870d4 IC |
61 | # |
62 | ||
0b8870d4 | 63 | def log_pif_action(action, pif): |
b3080599 | 64 | pifrec = db().get_pif_record(pif) |
0b8870d4 IC |
65 | rec = {} |
66 | rec['uuid'] = pifrec['uuid'] | |
67 | rec['ip_configuration_mode'] = pifrec['ip_configuration_mode'] | |
68 | rec['action'] = action | |
69 | rec['pif_netdev_name'] = pif_netdev_name(pif) | |
70 | rec['message'] = "Bring %(action)s PIF %(uuid)s" % rec | |
71 | log("%(message)s: %(pif_netdev_name)s configured as %(ip_configuration_mode)s" % rec) | |
72 | ||
0b8870d4 IC |
73 | # |
74 | # Exceptions. | |
75 | # | |
76 | ||
064af421 BP |
77 | class Usage(Exception): |
78 | def __init__(self, msg): | |
79 | Exception.__init__(self) | |
80 | self.msg = msg | |
81 | ||
0b8870d4 IC |
82 | # |
83 | # Boot from Network filesystem or device. | |
84 | # | |
064af421 | 85 | |
0b8870d4 IC |
86 | def check_allowed(pif): |
87 | """Determine whether interface-reconfigure should be manipulating this PIF. | |
064af421 | 88 | |
0b8870d4 IC |
89 | Used to prevent system PIFs (such as network root disk) from being interfered with. |
90 | """ | |
6dd3fad4 | 91 | |
b3080599 | 92 | pifrec = db().get_pif_record(pif) |
0b8870d4 | 93 | try: |
64ddb6fe | 94 | f = open(root_prefix() + "/proc/ardence") |
0b8870d4 IC |
95 | macline = filter(lambda x: x.startswith("HWaddr:"), f.readlines()) |
96 | f.close() | |
97 | if len(macline) == 1: | |
98 | p = re.compile(".*\s%(MAC)s\s.*" % pifrec, re.IGNORECASE) | |
99 | if p.match(macline[0]): | |
100 | log("Skipping PVS device %(device)s (%(MAC)s)" % pifrec) | |
101 | return False | |
102 | except IOError: | |
103 | pass | |
104 | return True | |
064af421 | 105 | |
0b8870d4 IC |
106 | # |
107 | # Bare Network Devices -- network devices without IP configuration | |
108 | # | |
064af421 | 109 | |
0b8870d4 IC |
110 | def netdev_remap_name(pif, already_renamed=[]): |
111 | """Check whether 'pif' exists and has the correct MAC. | |
112 | If not, try to find a device with the correct MAC and rename it. | |
113 | 'already_renamed' is used to avoid infinite recursion. | |
114 | """ | |
b3080599 | 115 | |
0b8870d4 IC |
116 | def read1(name): |
117 | file = None | |
118 | try: | |
119 | file = open(name, 'r') | |
120 | return file.readline().rstrip('\n') | |
121 | finally: | |
122 | if file != None: | |
123 | file.close() | |
064af421 | 124 | |
0b8870d4 IC |
125 | def get_netdev_mac(device): |
126 | try: | |
64ddb6fe | 127 | return read1("%s/sys/class/net/%s/address" % (root_prefix(), device)) |
0b8870d4 IC |
128 | except: |
129 | # Probably no such device. | |
130 | return None | |
064af421 | 131 | |
0b8870d4 IC |
132 | def get_netdev_tx_queue_len(device): |
133 | try: | |
64ddb6fe | 134 | return int(read1("%s/sys/class/net/%s/tx_queue_len" % (root_prefix(), device))) |
0b8870d4 IC |
135 | except: |
136 | # Probably no such device. | |
137 | return None | |
064af421 | 138 | |
0b8870d4 | 139 | def get_netdev_by_mac(mac): |
64ddb6fe | 140 | for device in os.listdir(root_prefix() + "/sys/class/net"): |
0b8870d4 IC |
141 | dev_mac = get_netdev_mac(device) |
142 | if (dev_mac and mac.lower() == dev_mac.lower() and | |
143 | get_netdev_tx_queue_len(device)): | |
144 | return device | |
145 | return None | |
064af421 | 146 | |
0b8870d4 | 147 | def rename_netdev(old_name, new_name): |
acfb4e95 BP |
148 | raise Error("Trying to rename %s to %s - This functionality has been removed" % (old_name, new_name)) |
149 | # log("Changing the name of %s to %s" % (old_name, new_name)) | |
150 | # run_command(['/sbin/ifconfig', old_name, 'down']) | |
151 | # if not run_command(['/sbin/ip', 'link', 'set', old_name, 'name', new_name]): | |
152 | # raise Error("Could not rename %s to %s" % (old_name, new_name)) | |
064af421 | 153 | |
b3080599 | 154 | pifrec = db().get_pif_record(pif) |
2bb451b6 BP |
155 | device = pifrec['device'] |
156 | mac = pifrec['MAC'] | |
157 | ||
158 | # Is there a network device named 'device' at all? | |
0b8870d4 | 159 | device_exists = netdev_exists(device) |
2bb451b6 BP |
160 | if device_exists: |
161 | # Yes. Does it have MAC 'mac'? | |
162 | found_mac = get_netdev_mac(device) | |
163 | if found_mac and mac.lower() == found_mac.lower(): | |
164 | # Yes, everything checks out the way we want. Nothing to do. | |
165 | return | |
166 | else: | |
167 | log("No network device %s" % device) | |
168 | ||
169 | # What device has MAC 'mac'? | |
170 | cur_device = get_netdev_by_mac(mac) | |
171 | if not cur_device: | |
172 | log("No network device has MAC %s" % mac) | |
173 | return | |
174 | ||
175 | # First rename 'device', if it exists, to get it out of the way | |
176 | # for 'cur_device' to replace it. | |
177 | if device_exists: | |
178 | rename_netdev(device, "dev%d" % random.getrandbits(24)) | |
179 | ||
180 | # Rename 'cur_device' to 'device'. | |
181 | rename_netdev(cur_device, device) | |
182 | ||
0b8870d4 IC |
183 | # |
184 | # IP Network Devices -- network devices with IP configuration | |
185 | # | |
064af421 | 186 | |
0b8870d4 IC |
187 | def ifdown(netdev): |
188 | """Bring down a network interface""" | |
0b8870d4 IC |
189 | if not netdev_exists(netdev): |
190 | log("ifdown: device %s does not exist, ignoring" % netdev) | |
191 | return | |
64ddb6fe | 192 | if not os.path.exists("%s/etc/sysconfig/network-scripts/ifcfg-%s" % (root_prefix(), netdev)): |
b3080599 IC |
193 | log("ifdown: device %s exists but ifcfg-%s does not" % (netdev,netdev)) |
194 | run_command(["/sbin/ifconfig", netdev, 'down']) | |
195 | return | |
0b8870d4 | 196 | run_command(["/sbin/ifdown", netdev]) |
064af421 | 197 | |
0b8870d4 IC |
198 | def ifup(netdev): |
199 | """Bring up a network interface""" | |
64ddb6fe | 200 | if not os.path.exists(root_prefix() + "/etc/sysconfig/network-scripts/ifcfg-%s" % netdev): |
0b8870d4 | 201 | raise Error("ifup: device %s exists but ifcfg-%s does not" % (netdev,netdev)) |
acfb4e95 BP |
202 | d = os.getenv("DHCLIENTARGS","") |
203 | if os.path.exists("/etc/firstboot.d/data/firstboot_in_progress"): | |
204 | os.putenv("DHCLIENTARGS", d + " -T 240 " ) | |
0b8870d4 | 205 | run_command(["/sbin/ifup", netdev]) |
acfb4e95 | 206 | os.putenv("DHCLIENTARGS", d ) |
0b8870d4 IC |
207 | |
208 | # | |
b3080599 | 209 | # |
0b8870d4 | 210 | # |
064af421 | 211 | |
b3080599 | 212 | def pif_rename_physical_devices(pif): |
92e906e4 IC |
213 | if pif_is_tunnel(pif): |
214 | return | |
064af421 | 215 | |
0b8870d4 | 216 | if pif_is_vlan(pif): |
b3080599 | 217 | pif = pif_get_vlan_slave(pif) |
064af421 | 218 | |
b3080599 IC |
219 | if pif_is_bond(pif): |
220 | pifs = pif_get_bond_slaves(pif) | |
0b8870d4 | 221 | else: |
b3080599 | 222 | pifs = [pif] |
0b8870d4 | 223 | |
b3080599 IC |
224 | for pif in pifs: |
225 | netdev_remap_name(pif) | |
0b8870d4 IC |
226 | |
227 | # | |
228 | # IP device configuration | |
229 | # | |
064af421 | 230 | |
0b8870d4 IC |
231 | def ipdev_configure_static_routes(interface, oc, f): |
232 | """Open a route-<interface> file for static routes. | |
233 | ||
234 | Opens the static routes configuration file for interface and writes one | |
235 | line for each route specified in the network's other config "static-routes" value. | |
236 | E.g. if | |
237 | interface ( RO): xenbr1 | |
238 | other-config (MRW): static-routes: 172.16.0.0/15/192.168.0.3,172.18.0.0/16/192.168.0.4;... | |
239 | ||
240 | Then route-xenbr1 should be | |
241 | 172.16.0.0/15 via 192.168.0.3 dev xenbr1 | |
242 | 172.18.0.0/16 via 192.168.0.4 dev xenbr1 | |
243 | """ | |
0b8870d4 IC |
244 | if oc.has_key('static-routes'): |
245 | # The key is present - extract comma seperates entries | |
246 | lines = oc['static-routes'].split(',') | |
247 | else: | |
248 | # The key is not present, i.e. there are no static routes | |
249 | lines = [] | |
250 | ||
64ddb6fe | 251 | child = ConfigurationFile("%s/etc/sysconfig/network-scripts/route-%s" % (root_prefix(), interface)) |
0b8870d4 IC |
252 | child.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \ |
253 | (os.path.basename(child.path()), os.path.basename(sys.argv[0]))) | |
254 | ||
255 | try: | |
256 | for l in lines: | |
257 | network, masklen, gateway = l.split('/') | |
258 | child.write("%s/%s via %s dev %s\n" % (network, masklen, gateway, interface)) | |
259 | ||
260 | f.attach_child(child) | |
261 | child.close() | |
262 | ||
263 | except ValueError, e: | |
264 | log("Error in other-config['static-routes'] format for network %s: %s" % (interface, e)) | |
064af421 | 265 | |
0b8870d4 IC |
266 | def ipdev_open_ifcfg(pif): |
267 | ipdev = pif_ipdev_name(pif) | |
c87d1024 | 268 | |
0b8870d4 IC |
269 | log("Writing network configuration for %s" % ipdev) |
270 | ||
64ddb6fe | 271 | f = ConfigurationFile("%s/etc/sysconfig/network-scripts/ifcfg-%s" % (root_prefix(), ipdev)) |
0b8870d4 IC |
272 | |
273 | f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \ | |
274 | (os.path.basename(f.path()), os.path.basename(sys.argv[0]))) | |
275 | f.write("XEMANAGED=yes\n") | |
276 | f.write("DEVICE=%s\n" % ipdev) | |
277 | f.write("ONBOOT=no\n") | |
acfb4e95 | 278 | f.write("NOZEROCONF=yes\n") |
0b8870d4 IC |
279 | |
280 | return f | |
281 | ||
b3080599 | 282 | def ipdev_configure_network(pif, dp): |
0b8870d4 IC |
283 | """Write the configuration file for a network. |
284 | ||
285 | Writes configuration derived from the network object into the relevant | |
286 | ifcfg file. The configuration file is passed in, but if the network is | |
287 | bridgeless it will be ifcfg-<interface>, otherwise it will be ifcfg-<bridge>. | |
288 | ||
289 | This routine may also write ifcfg files of the networks corresponding to other PIFs | |
290 | in order to maintain consistency. | |
291 | ||
292 | params: | |
293 | pif: Opaque_ref of pif | |
b3080599 | 294 | dp: Datapath object |
0b8870d4 IC |
295 | """ |
296 | ||
b3080599 | 297 | pifrec = db().get_pif_record(pif) |
9a2b1175 IC |
298 | nw = pifrec['network'] |
299 | nwrec = db().get_network_record(nw) | |
0b8870d4 IC |
300 | |
301 | ipdev = pif_ipdev_name(pif) | |
302 | ||
303 | f = ipdev_open_ifcfg(pif) | |
304 | ||
305 | mode = pifrec['ip_configuration_mode'] | |
306 | log("Configuring %s using %s configuration" % (ipdev, mode)) | |
307 | ||
308 | oc = None | |
309 | if pifrec.has_key('other_config'): | |
310 | oc = pifrec['other_config'] | |
311 | ||
b3080599 IC |
312 | dp.configure_ipdev(f) |
313 | ||
064af421 | 314 | if pifrec['ip_configuration_mode'] == "DHCP": |
0b8870d4 IC |
315 | f.write("BOOTPROTO=dhcp\n") |
316 | f.write("PERSISTENT_DHCLIENT=yes\n") | |
064af421 | 317 | elif pifrec['ip_configuration_mode'] == "Static": |
0b8870d4 IC |
318 | f.write("BOOTPROTO=none\n") |
319 | f.write("NETMASK=%(netmask)s\n" % pifrec) | |
320 | f.write("IPADDR=%(IP)s\n" % pifrec) | |
321 | f.write("GATEWAY=%(gateway)s\n" % pifrec) | |
064af421 | 322 | elif pifrec['ip_configuration_mode'] == "None": |
0b8870d4 | 323 | f.write("BOOTPROTO=none\n") |
064af421 | 324 | else: |
0b8870d4 IC |
325 | raise Error("Unknown ip-configuration-mode %s" % pifrec['ip_configuration_mode']) |
326 | ||
327 | if nwrec.has_key('other_config'): | |
328 | settings,offload = ethtool_settings(nwrec['other_config']) | |
329 | if len(settings): | |
330 | f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings)) | |
331 | if len(offload): | |
332 | f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload)) | |
333 | ||
0b8870d4 IC |
334 | ipdev_configure_static_routes(ipdev, nwrec['other_config'], f) |
335 | ||
9a2b1175 IC |
336 | mtu = mtu_setting(nw, "Network", nwrec['other_config']) |
337 | if mtu: | |
338 | f.write("MTU=%s\n" % mtu) | |
339 | ||
340 | ||
0b8870d4 IC |
341 | if pifrec.has_key('DNS') and pifrec['DNS'] != "": |
342 | ServerList = pifrec['DNS'].split(",") | |
343 | for i in range(len(ServerList)): f.write("DNS%d=%s\n" % (i+1, ServerList[i])) | |
344 | if oc and oc.has_key('domain'): | |
345 | f.write("DOMAIN='%s'\n" % oc['domain'].replace(',', ' ')) | |
346 | ||
b3080599 | 347 | # There can be only one DNSDEV and one GATEWAYDEV in /etc/sysconfig/network. |
0b8870d4 IC |
348 | # |
349 | # The peerdns pif will be the one with | |
350 | # pif::other-config:peerdns=true, or the mgmt pif if none have | |
351 | # this set. | |
352 | # | |
353 | # The gateway pif will be the one with | |
354 | # pif::other-config:defaultroute=true, or the mgmt pif if none | |
355 | # have this set. | |
356 | ||
b3080599 IC |
357 | # Work out which pif on this host should be the DNSDEV and which |
358 | # should be the GATEWAYDEV | |
0b8870d4 | 359 | # |
b3080599 | 360 | # Note: we prune out the bond master pif (if it exists). This is |
0b8870d4 IC |
361 | # because when we are called to bring up an interface with a bond |
362 | # master, it is implicit that we should bring down that master. | |
0b8870d4 | 363 | |
b3080599 | 364 | pifs_on_host = [p for p in db().get_all_pifs() if not p in pif_get_bond_masters(pif)] |
0b8870d4 | 365 | |
acfb4e95 BP |
366 | # now prune out bond slaves as they are not connected to the IP |
367 | # stack and so cannot be used as gateway or DNS devices. | |
368 | pifs_on_host = [ p for p in pifs_on_host if len(pif_get_bond_masters(p)) == 0] | |
369 | ||
0b8870d4 IC |
370 | # loop through all the pifs on this host looking for one with |
371 | # other-config:peerdns = true, and one with | |
372 | # other-config:default-route=true | |
b3080599 IC |
373 | peerdns_pif = None |
374 | defaultroute_pif = None | |
0b8870d4 | 375 | for __pif in pifs_on_host: |
b3080599 | 376 | __pifrec = db().get_pif_record(__pif) |
0b8870d4 IC |
377 | __oc = __pifrec['other_config'] |
378 | if __oc.has_key('peerdns') and __oc['peerdns'] == 'true': | |
379 | if peerdns_pif == None: | |
380 | peerdns_pif = __pif | |
381 | else: | |
382 | log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \ | |
b3080599 | 383 | (db().get_pif_record(peerdns_pif)['device'], __pifrec['device'])) |
0b8870d4 IC |
384 | if __oc.has_key('defaultroute') and __oc['defaultroute'] == 'true': |
385 | if defaultroute_pif == None: | |
386 | defaultroute_pif = __pif | |
387 | else: | |
388 | log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \ | |
b3080599 | 389 | (db().get_pif_record(defaultroute_pif)['device'], __pifrec['device'])) |
0b8870d4 IC |
390 | |
391 | # If no pif is explicitly specified then use the mgmt pif for | |
392 | # peerdns/defaultroute. | |
393 | if peerdns_pif == None: | |
394 | peerdns_pif = management_pif | |
395 | if defaultroute_pif == None: | |
396 | defaultroute_pif = management_pif | |
397 | ||
b3080599 IC |
398 | is_dnsdev = peerdns_pif == pif |
399 | is_gatewaydev = defaultroute_pif == pif | |
400 | ||
401 | if is_dnsdev or is_gatewaydev: | |
64ddb6fe | 402 | fnetwork = ConfigurationFile(root_prefix() + "/etc/sysconfig/network") |
b3080599 IC |
403 | for line in fnetwork.readlines(): |
404 | if is_dnsdev and line.lstrip().startswith('DNSDEV='): | |
405 | fnetwork.write('DNSDEV=%s\n' % ipdev) | |
406 | is_dnsdev = False | |
407 | elif is_gatewaydev and line.lstrip().startswith('GATEWAYDEV='): | |
408 | fnetwork.write('GATEWAYDEV=%s\n' % ipdev) | |
409 | is_gatewaydev = False | |
410 | else: | |
411 | fnetwork.write(line) | |
064af421 | 412 | |
b3080599 IC |
413 | if is_dnsdev: |
414 | fnetwork.write('DNSDEV=%s\n' % ipdev) | |
415 | if is_gatewaydev: | |
416 | fnetwork.write('GATEWAYDEV=%s\n' % ipdev) | |
0b8870d4 | 417 | |
b3080599 IC |
418 | fnetwork.close() |
419 | f.attach_child(fnetwork) | |
064af421 | 420 | |
b3080599 | 421 | return f |
064af421 | 422 | |
0b8870d4 | 423 | # |
b3080599 | 424 | # Toplevel actions |
0b8870d4 | 425 | # |
064af421 | 426 | |
b3080599 IC |
427 | def action_up(pif, force): |
428 | pifrec = db().get_pif_record(pif) | |
0b8870d4 | 429 | |
b3080599 | 430 | ipdev = pif_ipdev_name(pif) |
823c5699 | 431 | dp = DatapathFactory()(pif) |
0b8870d4 | 432 | |
b3080599 | 433 | log("action_up: %s" % ipdev) |
0b8870d4 | 434 | |
b3080599 | 435 | f = ipdev_configure_network(pif, dp) |
0b8870d4 | 436 | |
b3080599 | 437 | dp.preconfigure(f) |
0b8870d4 | 438 | |
b3080599 | 439 | f.close() |
0b8870d4 | 440 | |
b3080599 | 441 | pif_rename_physical_devices(pif) |
0b8870d4 | 442 | |
b3080599 IC |
443 | # if we are not forcing the interface up then attempt to tear down |
444 | # any existing devices which might interfere with brinign this one | |
445 | # up. | |
446 | if not force: | |
447 | ifdown(ipdev) | |
0b8870d4 | 448 | |
b3080599 | 449 | dp.bring_down_existing() |
0b8870d4 | 450 | |
064af421 BP |
451 | try: |
452 | f.apply() | |
0b8870d4 | 453 | |
b3080599 | 454 | dp.configure() |
0b8870d4 IC |
455 | |
456 | ifup(ipdev) | |
457 | ||
b3080599 | 458 | dp.post() |
0b8870d4 IC |
459 | |
460 | # Update /etc/issue (which contains the IP address of the management interface) | |
64ddb6fe | 461 | os.system(root_prefix() + "/sbin/update-issue") |
0b8870d4 | 462 | |
064af421 BP |
463 | f.commit() |
464 | except Error, e: | |
0b8870d4 | 465 | log("failed to apply changes: %s" % e.msg) |
064af421 BP |
466 | f.revert() |
467 | raise | |
468 | ||
0b8870d4 | 469 | def action_down(pif): |
0b8870d4 | 470 | ipdev = pif_ipdev_name(pif) |
823c5699 | 471 | dp = DatapathFactory()(pif) |
0b8870d4 | 472 | |
b3080599 | 473 | log("action_down: %s" % ipdev) |
0b8870d4 | 474 | |
b3080599 | 475 | ifdown(ipdev) |
0b8870d4 | 476 | |
b3080599 | 477 | dp.bring_down() |
064af421 | 478 | |
823c5699 IC |
479 | def action_rewrite(): |
480 | DatapathFactory().rewrite() | |
481 | ||
b3080599 IC |
482 | # This is useful for reconfiguring the mgmt interface after having lost connectivity to the pool master |
483 | def action_force_rewrite(bridge, config): | |
484 | def getUUID(): | |
485 | import subprocess | |
486 | uuid,_ = subprocess.Popen(['uuidgen'], stdout = subprocess.PIPE).communicate() | |
487 | return uuid.strip() | |
064af421 | 488 | |
b3080599 IC |
489 | # Notes: |
490 | # 1. that this assumes the interface is bridged | |
491 | # 2. If --gateway is given it will make that the default gateway for the host | |
064af421 | 492 | |
b3080599 | 493 | # extract the configuration |
0b8870d4 | 494 | try: |
b3080599 IC |
495 | mode = config['mode'] |
496 | mac = config['mac'] | |
497 | interface = config['device'] | |
498 | except: | |
499 | raise Usage("Please supply --mode, --mac and --device") | |
064af421 | 500 | |
b3080599 IC |
501 | if mode == 'static': |
502 | try: | |
503 | netmask = config['netmask'] | |
504 | ip = config['ip'] | |
505 | except: | |
506 | raise Usage("Please supply --netmask and --ip") | |
507 | try: | |
508 | gateway = config['gateway'] | |
509 | except: | |
510 | gateway = None | |
511 | elif mode != 'dhcp': | |
512 | raise Usage("--mode must be either static or dhcp") | |
064af421 | 513 | |
b3080599 IC |
514 | if config.has_key('vlan'): |
515 | is_vlan = True | |
516 | vlan_slave, vlan_vid = config['vlan'].split('.') | |
517 | else: | |
518 | is_vlan = False | |
519 | ||
520 | if is_vlan: | |
521 | raise Error("Force rewrite of VLAN not implemented") | |
522 | ||
523 | log("Configuring %s using %s configuration" % (bridge, mode)) | |
524 | ||
64ddb6fe | 525 | f = ConfigurationFile(root_prefix() + dbcache_file) |
b3080599 IC |
526 | |
527 | pif_uuid = getUUID() | |
528 | network_uuid = getUUID() | |
529 | ||
530 | f.write('<?xml version="1.0" ?>\n') | |
531 | f.write('<xenserver-network-configuration>\n') | |
532 | f.write('\t<pif ref="OpaqueRef:%s">\n' % pif_uuid) | |
533 | f.write('\t\t<network>OpaqueRef:%s</network>\n' % network_uuid) | |
534 | f.write('\t\t<management>True</management>\n') | |
535 | f.write('\t\t<uuid>%sPif</uuid>\n' % interface) | |
536 | f.write('\t\t<bond_slave_of>OpaqueRef:NULL</bond_slave_of>\n') | |
537 | f.write('\t\t<bond_master_of/>\n') | |
538 | f.write('\t\t<VLAN_slave_of/>\n') | |
539 | f.write('\t\t<VLAN_master_of>OpaqueRef:NULL</VLAN_master_of>\n') | |
540 | f.write('\t\t<VLAN>-1</VLAN>\n') | |
404c1692 AE |
541 | f.write('\t\t<tunnel_access_PIF_of/>\n') |
542 | f.write('\t\t<tunnel_transport_PIF_of/>\n') | |
b3080599 IC |
543 | f.write('\t\t<device>%s</device>\n' % interface) |
544 | f.write('\t\t<MAC>%s</MAC>\n' % mac) | |
545 | f.write('\t\t<other_config/>\n') | |
546 | if mode == 'dhcp': | |
547 | f.write('\t\t<ip_configuration_mode>DHCP</ip_configuration_mode>\n') | |
548 | f.write('\t\t<IP></IP>\n') | |
549 | f.write('\t\t<netmask></netmask>\n') | |
550 | f.write('\t\t<gateway></gateway>\n') | |
551 | f.write('\t\t<DNS></DNS>\n') | |
552 | elif mode == 'static': | |
553 | f.write('\t\t<ip_configuration_mode>Static</ip_configuration_mode>\n') | |
554 | f.write('\t\t<IP>%s</IP>\n' % ip) | |
555 | f.write('\t\t<netmask>%s</netmask>\n' % netmask) | |
556 | if gateway is not None: | |
557 | f.write('\t\t<gateway>%s</gateway>\n' % gateway) | |
558 | f.write('\t\t<DNS></DNS>\n') | |
559 | else: | |
560 | raise Error("Unknown mode %s" % mode) | |
561 | f.write('\t</pif>\n') | |
562 | ||
563 | f.write('\t<network ref="OpaqueRef:%s">\n' % network_uuid) | |
564 | f.write('\t\t<uuid>InitialManagementNetwork</uuid>\n') | |
565 | f.write('\t\t<PIFs>\n') | |
566 | f.write('\t\t\t<PIF>OpaqueRef:%s</PIF>\n' % pif_uuid) | |
567 | f.write('\t\t</PIFs>\n') | |
568 | f.write('\t\t<bridge>%s</bridge>\n' % bridge) | |
569 | f.write('\t\t<other_config/>\n') | |
570 | f.write('\t</network>\n') | |
571 | f.write('</xenserver-network-configuration>\n') | |
064af421 | 572 | |
064af421 | 573 | f.close() |
b3080599 | 574 | |
064af421 BP |
575 | try: |
576 | f.apply() | |
577 | f.commit() | |
578 | except Error, e: | |
579 | log("failed to apply changes: %s" % e.msg) | |
580 | f.revert() | |
581 | raise | |
582 | ||
064af421 | 583 | def main(argv=None): |
b3080599 | 584 | global management_pif |
0b8870d4 | 585 | |
064af421 BP |
586 | session = None |
587 | pif_uuid = None | |
588 | pif = None | |
589 | ||
590 | force_interface = None | |
591 | force_management = False | |
0b8870d4 | 592 | |
064af421 BP |
593 | if argv is None: |
594 | argv = sys.argv | |
595 | ||
596 | try: | |
597 | try: | |
598 | shortops = "h" | |
b3080599 | 599 | longops = [ "pif=", "pif-uuid=", |
064af421 BP |
600 | "session=", |
601 | "force=", | |
602 | "force-interface=", | |
603 | "management", | |
b3080599 | 604 | "mac=", "device=", "mode=", "ip=", "netmask=", "gateway=", |
64ddb6fe | 605 | "root-prefix=", |
b63fadcf | 606 | "no-syslog", |
064af421 BP |
607 | "help" ] |
608 | arglist, args = getopt.gnu_getopt(argv[1:], shortops, longops) | |
609 | except getopt.GetoptError, msg: | |
610 | raise Usage(msg) | |
611 | ||
612 | force_rewrite_config = {} | |
0b8870d4 | 613 | |
064af421 | 614 | for o,a in arglist: |
b3080599 | 615 | if o == "--pif": |
064af421 BP |
616 | pif = a |
617 | elif o == "--pif-uuid": | |
618 | pif_uuid = a | |
619 | elif o == "--session": | |
620 | session = a | |
621 | elif o == "--force-interface" or o == "--force": | |
622 | force_interface = a | |
623 | elif o == "--management": | |
624 | force_management = True | |
b3080599 | 625 | elif o in ["--mac", "--device", "--mode", "--ip", "--netmask", "--gateway"]: |
064af421 | 626 | force_rewrite_config[o[2:]] = a |
64ddb6fe BP |
627 | elif o == "--root-prefix": |
628 | set_root_prefix(a) | |
b63fadcf BP |
629 | elif o == "--no-syslog": |
630 | set_log_destination("stderr") | |
064af421 BP |
631 | elif o == "-h" or o == "--help": |
632 | print __doc__ % {'command-name': os.path.basename(argv[0])} | |
633 | return 0 | |
634 | ||
b63fadcf BP |
635 | if get_log_destination() == "syslog": |
636 | syslog.openlog(os.path.basename(argv[0])) | |
637 | log("Called as " + str.join(" ", argv)) | |
b3080599 | 638 | |
064af421 BP |
639 | if len(args) < 1: |
640 | raise Usage("Required option <action> not present") | |
641 | if len(args) > 1: | |
642 | raise Usage("Too many arguments") | |
643 | ||
644 | action = args[0] | |
fa2bec94 IC |
645 | |
646 | if not action in ["up", "down", "rewrite", "rewrite-configuration"]: | |
647 | raise Usage("Unknown action \"%s\"" % action) | |
648 | ||
064af421 BP |
649 | # backwards compatibility |
650 | if action == "rewrite-configuration": action = "rewrite" | |
0b8870d4 | 651 | |
064af421 BP |
652 | if ( session or pif ) and pif_uuid: |
653 | raise Usage("--session/--pif and --pif-uuid are mutually exclusive.") | |
654 | if ( session and not pif ) or ( not session and pif ): | |
655 | raise Usage("--session and --pif must be used together.") | |
656 | if force_interface and ( session or pif or pif_uuid ): | |
657 | raise Usage("--force is mutually exclusive with --session, --pif and --pif-uuid") | |
658 | if len(force_rewrite_config) and not (force_interface and action == "rewrite"): | |
659 | raise Usage("\"--force rewrite\" needed for --device, --mode, --ip, --netmask, and --gateway") | |
b3080599 IC |
660 | if (action == "rewrite") and (pif or pif_uuid ): |
661 | raise Usage("rewrite action does not take --pif or --pif-uuid") | |
662 | ||
064af421 BP |
663 | global db |
664 | if force_interface: | |
665 | log("Force interface %s %s" % (force_interface, action)) | |
666 | ||
667 | if action == "rewrite": | |
668 | action_force_rewrite(force_interface, force_rewrite_config) | |
fa2bec94 | 669 | elif action in ["up", "down"]: |
b3080599 IC |
670 | db_init_from_cache(dbcache_file) |
671 | pif = db().get_pif_by_bridge(force_interface) | |
672 | management_pif = db().get_management_pif() | |
064af421 BP |
673 | |
674 | if action == "up": | |
b3080599 | 675 | action_up(pif, True) |
064af421 BP |
676 | elif action == "down": |
677 | action_down(pif) | |
fa2bec94 IC |
678 | else: |
679 | raise Error("Unknown action %s" % action) | |
064af421 | 680 | else: |
b3080599 | 681 | db_init_from_xenapi(session) |
064af421 BP |
682 | |
683 | if pif_uuid: | |
b3080599 | 684 | pif = db().get_pif_by_uuid(pif_uuid) |
064af421 | 685 | |
b3080599 | 686 | if action == "rewrite": |
823c5699 | 687 | action_rewrite() |
064af421 | 688 | else: |
057fed2b BP |
689 | if not pif: |
690 | raise Usage("No PIF given") | |
064af421 | 691 | |
057fed2b | 692 | if force_management: |
0b8870d4 | 693 | # pif is going to be the management pif |
057fed2b BP |
694 | management_pif = pif |
695 | else: | |
696 | # pif is not going to be the management pif. | |
697 | # Search DB cache for pif on same host with management=true | |
b3080599 IC |
698 | pifrec = db().get_pif_record(pif) |
699 | management_pif = db().get_management_pif() | |
064af421 | 700 | |
057fed2b | 701 | log_pif_action(action, pif) |
064af421 | 702 | |
057fed2b BP |
703 | if not check_allowed(pif): |
704 | return 0 | |
705 | ||
706 | if action == "up": | |
b3080599 | 707 | action_up(pif, False) |
057fed2b BP |
708 | elif action == "down": |
709 | action_down(pif) | |
057fed2b | 710 | else: |
fa2bec94 | 711 | raise Error("Unknown action %s" % action) |
064af421 BP |
712 | |
713 | # Save cache. | |
b3080599 | 714 | db().save(dbcache_file) |
0b8870d4 | 715 | |
064af421 BP |
716 | except Usage, err: |
717 | print >>sys.stderr, err.msg | |
718 | print >>sys.stderr, "For help use --help." | |
719 | return 2 | |
720 | except Error, err: | |
721 | log(err.msg) | |
722 | return 1 | |
064af421 | 723 | |
0b8870d4 | 724 | return 0 |
064af421 | 725 | |
064af421 BP |
726 | if __name__ == "__main__": |
727 | rc = 1 | |
728 | try: | |
729 | rc = main() | |
730 | except: | |
731 | ex = sys.exc_info() | |
732 | err = traceback.format_exception(*ex) | |
733 | for exline in err: | |
734 | log(exline) | |
735 | ||
b3080599 | 736 | syslog.closelog() |
0b8870d4 | 737 | |
064af421 | 738 | sys.exit(rc) |