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