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