bugtool: Collect "lacp/show" and "cfm/show" information.
[sliver-openvswitch.git] / xenserver / opt_xensource_libexec_interface-reconfigure
1 #!/usr/bin/python
2 #
3 # Copyright (c) 2008,2009 Citrix Systems, Inc.
4 #
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 #
15 """Usage:
16
17     %(command-name)s <PIF> up
18     %(command-name)s <PIF> down
19     %(command-name)s rewrite
20     %(command-name)s --force <BRIDGE> up
21     %(command-name)s --force <BRIDGE> down
22     %(command-name)s --force <BRIDGE> rewrite --device=<INTERFACE> --mac=<MAC-ADDRESS> <CONFIG>
23
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>]
30
31   Options:
32     --session           A session reference to use to access the xapi DB
33     --pif               A PIF reference within the session.
34     --pif-uuid          The UUID of a PIF.
35     --force             An interface name.
36     --root-prefix=DIR   Use DIR as alternate root directory (for testing).
37     --no-syslog         Write log messages to stderr instead of system log.
38 """
39
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
46 from InterfaceReconfigure import *
47
48 import os, sys, getopt
49 import syslog
50 import traceback
51 import re
52 import random
53
54 management_pif = None
55
56 dbcache_file = "/var/xapi/network.dbcache"
57
58 #
59 # Logging.
60 #
61
62 def log_pif_action(action, pif):
63     pifrec = db().get_pif_record(pif)
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
72 #
73 # Exceptions.
74 #
75
76 class Usage(Exception):
77     def __init__(self, msg):
78         Exception.__init__(self)
79         self.msg = msg
80
81 #
82 # Boot from Network filesystem or device.
83 #
84
85 def check_allowed(pif):
86     """Determine whether interface-reconfigure should be manipulating this PIF.
87
88     Used to prevent system PIFs (such as network root disk) from being interfered with.
89     """
90
91     pifrec = db().get_pif_record(pif)
92     try:
93         f = open(root_prefix() + "/proc/ardence")
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
104
105 #
106 # Bare Network Devices -- network devices without IP configuration
107 #
108
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     """
114
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()
123
124     def get_netdev_mac(device):
125         try:
126             return read1("%s/sys/class/net/%s/address" % (root_prefix(), device))
127         except:
128             # Probably no such device.
129             return None
130
131     def get_netdev_tx_queue_len(device):
132         try:
133             return int(read1("%s/sys/class/net/%s/tx_queue_len" % (root_prefix(), device)))
134         except:
135             # Probably no such device.
136             return None
137
138     def get_netdev_by_mac(mac):
139         for device in os.listdir(root_prefix() + "/sys/class/net"):
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
145
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))
151
152     pifrec = db().get_pif_record(pif)
153     device = pifrec['device']
154     mac = pifrec['MAC']
155
156     # Is there a network device named 'device' at all?
157     device_exists = netdev_exists(device)
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
181 #
182 # IP Network Devices -- network devices with IP configuration
183 #
184
185 def ifdown(netdev):
186     """Bring down a network interface"""
187     if not netdev_exists(netdev):
188         log("ifdown: device %s does not exist, ignoring" % netdev)
189         return
190     if not os.path.exists("%s/etc/sysconfig/network-scripts/ifcfg-%s" % (root_prefix(), netdev)):
191         log("ifdown: device %s exists but ifcfg-%s does not" % (netdev,netdev))
192         run_command(["/sbin/ifconfig", netdev, 'down'])
193         return
194     run_command(["/sbin/ifdown", netdev])
195
196 def ifup(netdev):
197     """Bring up a network interface"""
198     if not os.path.exists(root_prefix() + "/etc/sysconfig/network-scripts/ifcfg-%s" % netdev):
199         raise Error("ifup: device %s exists but ifcfg-%s does not" % (netdev,netdev))
200     run_command(["/sbin/ifup", netdev])
201
202 #
203 #
204 #
205
206 def pif_rename_physical_devices(pif):
207     if pif_is_tunnel(pif):
208         return
209
210     if pif_is_vlan(pif):
211         pif = pif_get_vlan_slave(pif)
212
213     if pif_is_bond(pif):
214         pifs = pif_get_bond_slaves(pif)
215     else:
216         pifs = [pif]
217
218     for pif in pifs:
219         netdev_remap_name(pif)
220
221 #
222 # IP device configuration
223 #
224
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     """
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
245     child = ConfigurationFile("%s/etc/sysconfig/network-scripts/route-%s" % (root_prefix(), interface))
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))
259
260 def ipdev_open_ifcfg(pif):
261     ipdev = pif_ipdev_name(pif)
262
263     log("Writing network configuration for %s" % ipdev)
264
265     f = ConfigurationFile("%s/etc/sysconfig/network-scripts/ifcfg-%s" % (root_prefix(), ipdev))
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
275 def ipdev_configure_network(pif, dp):
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
287         dp:   Datapath object
288     """
289
290     pifrec = db().get_pif_record(pif)
291     nw = pifrec['network']
292     nwrec = db().get_network_record(nw)
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
305     dp.configure_ipdev(f)
306
307     if pifrec['ip_configuration_mode'] == "DHCP":
308         f.write("BOOTPROTO=dhcp\n")
309         f.write("PERSISTENT_DHCLIENT=yes\n")
310     elif pifrec['ip_configuration_mode'] == "Static":
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)
315     elif pifrec['ip_configuration_mode'] == "None":
316         f.write("BOOTPROTO=none\n")
317     else:
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
327         ipdev_configure_static_routes(ipdev, nwrec['other_config'], f)
328
329     mtu = mtu_setting(nw, "Network", nwrec['other_config'])
330     if mtu:
331         f.write("MTU=%s\n" % mtu)
332
333
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
340     # There can be only one DNSDEV and one GATEWAYDEV in /etc/sysconfig/network.
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
350     # Work out which pif on this host should be the DNSDEV and which
351     # should be the GATEWAYDEV
352     #
353     # Note: we prune out the bond master pif (if it exists). This is
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.
356
357     pifs_on_host = [p for p in db().get_all_pifs() if not p in pif_get_bond_masters(pif)]
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
362     peerdns_pif = None
363     defaultroute_pif = None
364     for __pif in pifs_on_host:
365         __pifrec = db().get_pif_record(__pif)
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' % \
372                         (db().get_pif_record(peerdns_pif)['device'], __pifrec['device']))
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' % \
378                         (db().get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
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
387     is_dnsdev = peerdns_pif == pif
388     is_gatewaydev = defaultroute_pif == pif
389
390     if is_dnsdev or is_gatewaydev:
391         fnetwork = ConfigurationFile(root_prefix() + "/etc/sysconfig/network")
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)
401
402         if is_dnsdev:
403             fnetwork.write('DNSDEV=%s\n' % ipdev)
404         if is_gatewaydev:
405             fnetwork.write('GATEWAYDEV=%s\n' % ipdev)
406
407         fnetwork.close()
408         f.attach_child(fnetwork)
409
410     return f
411
412 #
413 # Toplevel actions
414 #
415
416 def action_up(pif, force):
417     pifrec = db().get_pif_record(pif)
418
419     ipdev = pif_ipdev_name(pif)
420     dp = DatapathFactory()(pif)
421
422     log("action_up: %s" % ipdev)
423
424     f = ipdev_configure_network(pif, dp)
425
426     dp.preconfigure(f)
427
428     f.close()
429
430     pif_rename_physical_devices(pif)
431
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)
437
438         dp.bring_down_existing()
439
440     try:
441         f.apply()
442
443         dp.configure()
444
445         ifup(ipdev)
446
447         dp.post()
448
449         # Update /etc/issue (which contains the IP address of the management interface)
450         os.system(root_prefix() + "/sbin/update-issue")
451
452         f.commit()
453     except Error, e:
454         log("failed to apply changes: %s" % e.msg)
455         f.revert()
456         raise
457
458 def action_down(pif):
459     ipdev = pif_ipdev_name(pif)
460     dp = DatapathFactory()(pif)
461
462     log("action_down: %s" % ipdev)
463
464     ifdown(ipdev)
465
466     dp.bring_down()
467
468 def action_rewrite():
469     DatapathFactory().rewrite()
470     
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()
477
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
481
482     # extract the configuration
483     try:
484         mode = config['mode']
485         mac = config['mac']
486         interface = config['device']
487     except:
488         raise Usage("Please supply --mode, --mac and --device")
489
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")
502
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
514     f = ConfigurationFile(root_prefix() + dbcache_file)
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')
530     f.write('\t\t<tunnel_access_PIF_of/>\n')
531     f.write('\t\t<tunnel_transport_PIF_of/>\n')
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')
561
562     f.close()
563
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
572 def main(argv=None):
573     global management_pif
574
575     session = None
576     pif_uuid = None
577     pif = None
578
579     force_interface = None
580     force_management = False
581
582     if argv is None:
583         argv = sys.argv
584
585     try:
586         try:
587             shortops = "h"
588             longops = [ "pif=", "pif-uuid=",
589                         "session=",
590                         "force=",
591                         "force-interface=",
592                         "management",
593                         "mac=", "device=", "mode=", "ip=", "netmask=", "gateway=",
594                         "root-prefix=",
595                         "no-syslog",
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 = {}
602
603         for o,a in arglist:
604             if o == "--pif":
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
614             elif o in ["--mac", "--device", "--mode", "--ip", "--netmask", "--gateway"]:
615                 force_rewrite_config[o[2:]] = a
616             elif o == "--root-prefix":
617                 set_root_prefix(a)
618             elif o == "--no-syslog":
619                 set_log_destination("stderr")
620             elif o == "-h" or o == "--help":
621                 print __doc__ % {'command-name': os.path.basename(argv[0])}
622                 return 0
623
624         if get_log_destination() == "syslog":
625             syslog.openlog(os.path.basename(argv[0]))
626             log("Called as " + str.join(" ", argv))
627
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]
634
635         if not action in ["up", "down", "rewrite", "rewrite-configuration"]:
636             raise Usage("Unknown action \"%s\"" % action)
637
638         # backwards compatibility
639         if action == "rewrite-configuration": action = "rewrite"
640
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")
649         if (action == "rewrite") and (pif or pif_uuid ):
650             raise Usage("rewrite action does not take --pif or --pif-uuid")
651         
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)
658             elif action in ["up", "down"]:
659                 db_init_from_cache(dbcache_file)
660                 pif = db().get_pif_by_bridge(force_interface)
661                 management_pif = db().get_management_pif()
662
663                 if action == "up":
664                     action_up(pif, True)
665                 elif action == "down":
666                     action_down(pif)
667             else:
668                 raise Error("Unknown action %s"  % action)
669         else:
670             db_init_from_xenapi(session)
671
672             if pif_uuid:
673                 pif = db().get_pif_by_uuid(pif_uuid)
674
675             if action == "rewrite":
676                 action_rewrite()
677             else:
678                 if not pif:
679                     raise Usage("No PIF given")
680
681                 if force_management:
682                     # pif is going to be the management pif
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
687                     pifrec = db().get_pif_record(pif)
688                     management_pif = db().get_management_pif()
689
690                 log_pif_action(action, pif)
691
692                 if not check_allowed(pif):
693                     return 0
694
695                 if action == "up":
696                     action_up(pif, False)
697                 elif action == "down":
698                     action_down(pif)
699                 else:
700                     raise Error("Unknown action %s"  % action)
701
702             # Save cache.
703             db().save(dbcache_file)
704
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
712
713     return 0
714
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
725     syslog.closelog()
726
727     sys.exit(rc)