xenserver: Add --no-syslog feature to interface-reconfigure.
[cascardo/ovs.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
208     if pif_is_vlan(pif):
209         pif = pif_get_vlan_slave(pif)
210
211     if pif_is_bond(pif):
212         pifs = pif_get_bond_slaves(pif)
213     else:
214         pifs = [pif]
215
216     for pif in pifs:
217         netdev_remap_name(pif)
218
219 #
220 # IP device configuration
221 #
222
223 def ipdev_configure_static_routes(interface, oc, f):
224     """Open a route-<interface> file for static routes.
225
226     Opens the static routes configuration file for interface and writes one
227     line for each route specified in the network's other config "static-routes" value.
228     E.g. if
229            interface ( RO): xenbr1
230            other-config (MRW): static-routes: 172.16.0.0/15/192.168.0.3,172.18.0.0/16/192.168.0.4;...
231
232     Then route-xenbr1 should be
233           172.16.0.0/15 via 192.168.0.3 dev xenbr1
234           172.18.0.0/16 via 192.168.0.4 dev xenbr1
235     """
236     if oc.has_key('static-routes'):
237         # The key is present - extract comma seperates entries
238         lines = oc['static-routes'].split(',')
239     else:
240         # The key is not present, i.e. there are no static routes
241         lines = []
242
243     child = ConfigurationFile("%s/etc/sysconfig/network-scripts/route-%s" % (root_prefix(), interface))
244     child.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
245             (os.path.basename(child.path()), os.path.basename(sys.argv[0])))
246
247     try:
248         for l in lines:
249             network, masklen, gateway = l.split('/')
250             child.write("%s/%s via %s dev %s\n" % (network, masklen, gateway, interface))
251
252         f.attach_child(child)
253         child.close()
254
255     except ValueError, e:
256         log("Error in other-config['static-routes'] format for network %s: %s" % (interface, e))
257
258 def ipdev_open_ifcfg(pif):
259     ipdev = pif_ipdev_name(pif)
260
261     log("Writing network configuration for %s" % ipdev)
262
263     f = ConfigurationFile("%s/etc/sysconfig/network-scripts/ifcfg-%s" % (root_prefix(), ipdev))
264
265     f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
266             (os.path.basename(f.path()), os.path.basename(sys.argv[0])))
267     f.write("XEMANAGED=yes\n")
268     f.write("DEVICE=%s\n" % ipdev)
269     f.write("ONBOOT=no\n")
270
271     return f
272
273 def ipdev_configure_network(pif, dp):
274     """Write the configuration file for a network.
275
276     Writes configuration derived from the network object into the relevant
277     ifcfg file.  The configuration file is passed in, but if the network is
278     bridgeless it will be ifcfg-<interface>, otherwise it will be ifcfg-<bridge>.
279
280     This routine may also write ifcfg files of the networks corresponding to other PIFs
281     in order to maintain consistency.
282
283     params:
284         pif:  Opaque_ref of pif
285         dp:   Datapath object
286     """
287
288     pifrec = db().get_pif_record(pif)
289     nwrec = db().get_network_record(pifrec['network'])
290
291     ipdev = pif_ipdev_name(pif)
292
293     f = ipdev_open_ifcfg(pif)
294
295     mode = pifrec['ip_configuration_mode']
296     log("Configuring %s using %s configuration" % (ipdev, mode))
297
298     oc = None
299     if pifrec.has_key('other_config'):
300         oc = pifrec['other_config']
301
302     dp.configure_ipdev(f)
303
304     if pifrec['ip_configuration_mode'] == "DHCP":
305         f.write("BOOTPROTO=dhcp\n")
306         f.write("PERSISTENT_DHCLIENT=yes\n")
307     elif pifrec['ip_configuration_mode'] == "Static":
308         f.write("BOOTPROTO=none\n")
309         f.write("NETMASK=%(netmask)s\n" % pifrec)
310         f.write("IPADDR=%(IP)s\n" % pifrec)
311         f.write("GATEWAY=%(gateway)s\n" % pifrec)
312     elif pifrec['ip_configuration_mode'] == "None":
313         f.write("BOOTPROTO=none\n")
314     else:
315         raise Error("Unknown ip-configuration-mode %s" % pifrec['ip_configuration_mode'])
316
317     if nwrec.has_key('other_config'):
318         settings,offload = ethtool_settings(nwrec['other_config'])
319         if len(settings):
320             f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings))
321         if len(offload):
322             f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload))
323
324         mtu = mtu_setting(nwrec['other_config'])
325         if mtu:
326             f.write("MTU=%s\n" % mtu)
327
328         ipdev_configure_static_routes(ipdev, nwrec['other_config'], f)
329
330     if pifrec.has_key('DNS') and pifrec['DNS'] != "":
331         ServerList = pifrec['DNS'].split(",")
332         for i in range(len(ServerList)): f.write("DNS%d=%s\n" % (i+1, ServerList[i]))
333     if oc and oc.has_key('domain'):
334         f.write("DOMAIN='%s'\n" % oc['domain'].replace(',', ' '))
335
336     # There can be only one DNSDEV and one GATEWAYDEV in /etc/sysconfig/network.
337     #
338     # The peerdns pif will be the one with
339     # pif::other-config:peerdns=true, or the mgmt pif if none have
340     # this set.
341     #
342     # The gateway pif will be the one with
343     # pif::other-config:defaultroute=true, or the mgmt pif if none
344     # have this set.
345
346     # Work out which pif on this host should be the DNSDEV and which
347     # should be the GATEWAYDEV
348     #
349     # Note: we prune out the bond master pif (if it exists). This is
350     # because when we are called to bring up an interface with a bond
351     # master, it is implicit that we should bring down that master.
352
353     pifs_on_host = [p for p in db().get_all_pifs() if not p in pif_get_bond_masters(pif)]
354
355     # loop through all the pifs on this host looking for one with
356     #   other-config:peerdns = true, and one with
357     #   other-config:default-route=true
358     peerdns_pif = None
359     defaultroute_pif = None
360     for __pif in pifs_on_host:
361         __pifrec = db().get_pif_record(__pif)
362         __oc = __pifrec['other_config']
363         if __oc.has_key('peerdns') and __oc['peerdns'] == 'true':
364             if peerdns_pif == None:
365                 peerdns_pif = __pif
366             else:
367                 log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \
368                         (db().get_pif_record(peerdns_pif)['device'], __pifrec['device']))
369         if __oc.has_key('defaultroute') and __oc['defaultroute'] == 'true':
370             if defaultroute_pif == None:
371                 defaultroute_pif = __pif
372             else:
373                 log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \
374                         (db().get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
375
376     # If no pif is explicitly specified then use the mgmt pif for
377     # peerdns/defaultroute.
378     if peerdns_pif == None:
379         peerdns_pif = management_pif
380     if defaultroute_pif == None:
381         defaultroute_pif = management_pif
382
383     is_dnsdev = peerdns_pif == pif
384     is_gatewaydev = defaultroute_pif == pif
385
386     if is_dnsdev or is_gatewaydev:
387         fnetwork = ConfigurationFile(root_prefix() + "/etc/sysconfig/network")
388         for line in fnetwork.readlines():
389             if is_dnsdev and line.lstrip().startswith('DNSDEV='):
390                 fnetwork.write('DNSDEV=%s\n' % ipdev)
391                 is_dnsdev = False
392             elif is_gatewaydev and line.lstrip().startswith('GATEWAYDEV='):
393                 fnetwork.write('GATEWAYDEV=%s\n' % ipdev)
394                 is_gatewaydev = False
395             else:
396                 fnetwork.write(line)
397
398         if is_dnsdev:
399             fnetwork.write('DNSDEV=%s\n' % ipdev)
400         if is_gatewaydev:
401             fnetwork.write('GATEWAYDEV=%s\n' % ipdev)
402
403         fnetwork.close()
404         f.attach_child(fnetwork)
405
406     return f
407
408 #
409 # Toplevel actions
410 #
411
412 def action_up(pif, force):
413     pifrec = db().get_pif_record(pif)
414
415     ipdev = pif_ipdev_name(pif)
416     dp = DatapathFactory(pif)
417
418     log("action_up: %s" % ipdev)
419
420     f = ipdev_configure_network(pif, dp)
421
422     dp.preconfigure(f)
423
424     f.close()
425
426     pif_rename_physical_devices(pif)
427
428     # if we are not forcing the interface up then attempt to tear down
429     # any existing devices which might interfere with brinign this one
430     # up.
431     if not force:
432         ifdown(ipdev)
433
434         dp.bring_down_existing()
435
436     try:
437         f.apply()
438
439         dp.configure()
440
441         ifup(ipdev)
442
443         dp.post()
444
445         # Update /etc/issue (which contains the IP address of the management interface)
446         os.system(root_prefix() + "/sbin/update-issue")
447
448         f.commit()
449     except Error, e:
450         log("failed to apply changes: %s" % e.msg)
451         f.revert()
452         raise
453
454 def action_down(pif):
455     ipdev = pif_ipdev_name(pif)
456     dp = DatapathFactory(pif)
457
458     log("action_down: %s" % ipdev)
459
460     ifdown(ipdev)
461
462     dp.bring_down()
463
464 # This is useful for reconfiguring the mgmt interface after having lost connectivity to the pool master
465 def action_force_rewrite(bridge, config):
466     def getUUID():
467         import subprocess
468         uuid,_ = subprocess.Popen(['uuidgen'], stdout = subprocess.PIPE).communicate()
469         return uuid.strip()
470
471     # Notes:
472     # 1. that this assumes the interface is bridged
473     # 2. If --gateway is given it will make that the default gateway for the host
474
475     # extract the configuration
476     try:
477         mode = config['mode']
478         mac = config['mac']
479         interface = config['device']
480     except:
481         raise Usage("Please supply --mode, --mac and --device")
482
483     if mode == 'static':
484         try:
485             netmask = config['netmask']
486             ip = config['ip']
487         except:
488             raise Usage("Please supply --netmask and --ip")
489         try:
490             gateway = config['gateway']
491         except:
492             gateway = None
493     elif mode != 'dhcp':
494         raise Usage("--mode must be either static or dhcp")
495
496     if config.has_key('vlan'):
497         is_vlan = True
498         vlan_slave, vlan_vid = config['vlan'].split('.')
499     else:
500         is_vlan = False
501
502     if is_vlan:
503         raise Error("Force rewrite of VLAN not implemented")
504
505     log("Configuring %s using %s configuration" % (bridge, mode))
506
507     f = ConfigurationFile(root_prefix() + dbcache_file)
508
509     pif_uuid = getUUID()
510     network_uuid = getUUID()
511
512     f.write('<?xml version="1.0" ?>\n')
513     f.write('<xenserver-network-configuration>\n')
514     f.write('\t<pif ref="OpaqueRef:%s">\n' % pif_uuid)
515     f.write('\t\t<network>OpaqueRef:%s</network>\n' % network_uuid)
516     f.write('\t\t<management>True</management>\n')
517     f.write('\t\t<uuid>%sPif</uuid>\n' % interface)
518     f.write('\t\t<bond_slave_of>OpaqueRef:NULL</bond_slave_of>\n')
519     f.write('\t\t<bond_master_of/>\n')
520     f.write('\t\t<VLAN_slave_of/>\n')
521     f.write('\t\t<VLAN_master_of>OpaqueRef:NULL</VLAN_master_of>\n')
522     f.write('\t\t<VLAN>-1</VLAN>\n')
523     f.write('\t\t<device>%s</device>\n' % interface)
524     f.write('\t\t<MAC>%s</MAC>\n' % mac)
525     f.write('\t\t<other_config/>\n')
526     if mode == 'dhcp':
527         f.write('\t\t<ip_configuration_mode>DHCP</ip_configuration_mode>\n')
528         f.write('\t\t<IP></IP>\n')
529         f.write('\t\t<netmask></netmask>\n')
530         f.write('\t\t<gateway></gateway>\n')
531         f.write('\t\t<DNS></DNS>\n')
532     elif mode == 'static':
533         f.write('\t\t<ip_configuration_mode>Static</ip_configuration_mode>\n')
534         f.write('\t\t<IP>%s</IP>\n' % ip)
535         f.write('\t\t<netmask>%s</netmask>\n' % netmask)
536         if gateway is not None:
537             f.write('\t\t<gateway>%s</gateway>\n' % gateway)
538         f.write('\t\t<DNS></DNS>\n')
539     else:
540         raise Error("Unknown mode %s" % mode)
541     f.write('\t</pif>\n')
542
543     f.write('\t<network ref="OpaqueRef:%s">\n' % network_uuid)
544     f.write('\t\t<uuid>InitialManagementNetwork</uuid>\n')
545     f.write('\t\t<PIFs>\n')
546     f.write('\t\t\t<PIF>OpaqueRef:%s</PIF>\n' % pif_uuid)
547     f.write('\t\t</PIFs>\n')
548     f.write('\t\t<bridge>%s</bridge>\n' % bridge)
549     f.write('\t\t<other_config/>\n')
550     f.write('\t</network>\n')
551     f.write('</xenserver-network-configuration>\n')
552
553     f.close()
554
555     try:
556         f.apply()
557         f.commit()
558     except Error, e:
559         log("failed to apply changes: %s" % e.msg)
560         f.revert()
561         raise
562
563 def main(argv=None):
564     global management_pif
565
566     session = None
567     pif_uuid = None
568     pif = None
569
570     force_interface = None
571     force_management = False
572
573     if argv is None:
574         argv = sys.argv
575
576     try:
577         try:
578             shortops = "h"
579             longops = [ "pif=", "pif-uuid=",
580                         "session=",
581                         "force=",
582                         "force-interface=",
583                         "management",
584                         "mac=", "device=", "mode=", "ip=", "netmask=", "gateway=",
585                         "root-prefix=",
586                         "no-syslog",
587                         "help" ]
588             arglist, args = getopt.gnu_getopt(argv[1:], shortops, longops)
589         except getopt.GetoptError, msg:
590             raise Usage(msg)
591
592         force_rewrite_config = {}
593
594         for o,a in arglist:
595             if o == "--pif":
596                 pif = a
597             elif o == "--pif-uuid":
598                 pif_uuid = a
599             elif o == "--session":
600                 session = a
601             elif o == "--force-interface" or o == "--force":
602                 force_interface = a
603             elif o == "--management":
604                 force_management = True
605             elif o in ["--mac", "--device", "--mode", "--ip", "--netmask", "--gateway"]:
606                 force_rewrite_config[o[2:]] = a
607             elif o == "--root-prefix":
608                 set_root_prefix(a)
609             elif o == "--no-syslog":
610                 set_log_destination("stderr")
611             elif o == "-h" or o == "--help":
612                 print __doc__ % {'command-name': os.path.basename(argv[0])}
613                 return 0
614
615         if get_log_destination() == "syslog":
616             syslog.openlog(os.path.basename(argv[0]))
617             log("Called as " + str.join(" ", argv))
618
619         if len(args) < 1:
620             raise Usage("Required option <action> not present")
621         if len(args) > 1:
622             raise Usage("Too many arguments")
623
624         action = args[0]
625
626         if not action in ["up", "down", "rewrite", "rewrite-configuration"]:
627             raise Usage("Unknown action \"%s\"" % action)
628
629         # backwards compatibility
630         if action == "rewrite-configuration": action = "rewrite"
631
632         if ( session or pif ) and pif_uuid:
633             raise Usage("--session/--pif and --pif-uuid are mutually exclusive.")
634         if ( session and not pif ) or ( not session and pif ):
635             raise Usage("--session and --pif must be used together.")
636         if force_interface and ( session or pif or pif_uuid ):
637             raise Usage("--force is mutually exclusive with --session, --pif and --pif-uuid")
638         if len(force_rewrite_config) and not (force_interface and action == "rewrite"):
639             raise Usage("\"--force rewrite\" needed for --device, --mode, --ip, --netmask, and --gateway")
640         if (action == "rewrite") and (pif or pif_uuid ):
641             raise Usage("rewrite action does not take --pif or --pif-uuid")
642         
643         global db
644         if force_interface:
645             log("Force interface %s %s" % (force_interface, action))
646
647             if action == "rewrite":
648                 action_force_rewrite(force_interface, force_rewrite_config)
649             elif action in ["up", "down"]:
650                 db_init_from_cache(dbcache_file)
651                 pif = db().get_pif_by_bridge(force_interface)
652                 management_pif = db().get_management_pif()
653
654                 if action == "up":
655                     action_up(pif, True)
656                 elif action == "down":
657                     action_down(pif)
658             else:
659                 raise Error("Unknown action %s"  % action)
660         else:
661             db_init_from_xenapi(session)
662
663             if pif_uuid:
664                 pif = db().get_pif_by_uuid(pif_uuid)
665
666             if action == "rewrite":
667                 pass
668             else:
669                 if not pif:
670                     raise Usage("No PIF given")
671
672                 if force_management:
673                     # pif is going to be the management pif
674                     management_pif = pif
675                 else:
676                     # pif is not going to be the management pif.
677                     # Search DB cache for pif on same host with management=true
678                     pifrec = db().get_pif_record(pif)
679                     management_pif = db().get_management_pif()
680
681                 log_pif_action(action, pif)
682
683                 if not check_allowed(pif):
684                     return 0
685
686                 if action == "up":
687                     action_up(pif, False)
688                 elif action == "down":
689                     action_down(pif)
690                 else:
691                     raise Error("Unknown action %s"  % action)
692
693             # Save cache.
694             db().save(dbcache_file)
695
696     except Usage, err:
697         print >>sys.stderr, err.msg
698         print >>sys.stderr, "For help use --help."
699         return 2
700     except Error, err:
701         log(err.msg)
702         return 1
703
704     return 0
705
706 if __name__ == "__main__":
707     rc = 1
708     try:
709         rc = main()
710     except:
711         ex = sys.exc_info()
712         err = traceback.format_exception(*ex)
713         for exline in err:
714             log(exline)
715
716     syslog.closelog()
717
718     sys.exit(rc)