Add better distinction of error cases

Add a new NotFoundError exception for use internally, so we can track
specific failure cases.
Also add a new Error class as a central place to store our error
numbers consistently.
In the API protocol, add a "NOTFOUND" error string alongside "ERROR"
to help the admin interface and other callers distinguish error cases
better.
In the admin interface, actually return distinct non-zero errors in
failure cases. Previously, almost all failures would have returned
suceesfully to the calling shell.

Change-Id: Ie382b737a80b7cd41c551e3a4a2a7e0827260bdc
diff --git a/admin.py b/admin.py
index 1e833d1..26964f4 100755
--- a/admin.py
+++ b/admin.py
@@ -27,14 +27,14 @@
 vlandpath = os.path.abspath(os.path.normpath(os.path.dirname(sys.argv[0])))
 sys.path.insert(0, vlandpath)
 
-from errors import InputError, SocketError
+from errors import InputError, SocketError, NotFoundError, Error
 from config.config import VlanConfig
 from ipc.ipc import VlanIpc
 
 prog = "admin"
 version = "0.6-DEV"
 banner = "Linaro VLANd admin interface, version %s" % version
-
+exit = Error.OK
 TRUNK_ID_NONE = -1
 
 def is_positive(text):
@@ -85,8 +85,9 @@
     if 'response' not in ret:
         raise SocketError("Badly-formed response from VLANd server")
     if ret['response'] == "ERROR":
-        print "Input error: VLANd server said \"%s\"" % ret['error']
-        sys.exit(1)
+        raise InputError("VLANd server said \"%s\"" % ret['error'])
+    if ret['response'] == "NOTFOUND":
+        raise NotFoundError("VLANd server said \"%s\"" % ret['error'])
     return ret['data']
 
 config = VlanConfig(filenames=('./vland.cfg',))
@@ -366,71 +367,103 @@
 
 # Now work out what to do
 if args.which == 'status':
-    print 'Config:'
-    print '  knows about %d switch(es)' % len(config.switches)
-    default_vlan_id = call_vland('db_query',
-                                 {'command':'db.get_vlan_id_by_tag',
-                                  'data':
-                                  {'tag': config.vland.default_vlan_tag}})
-    print 'The default vlan tag (%d) is vlan ID %d' % (config.vland.default_vlan_tag, default_vlan_id)
-    stat = call_vland('daemon_query', {'command':'daemon.status', 'data': None})
-    print 'VLANd is running %s' % stat['running']
-    lastmod = datetime.datetime.strptime(stat['last_modified'], '%Y-%m-%dT%H:%M:%S.%f')
-    print 'DB Last modified %s' % lastmod.strftime('%Y-%m-%d %H:%M:%S %Z')
-    print 'DB via VLANd:'
-    switches = call_vland('db_query', {'command':'db.all_switches', 'data':None})
-    print '  knows about %d switch(es)' % len(switches)
-    ports = call_vland('db_query', {'command':'db.all_ports', 'data':None})
-    print '  knows about %d port(s)' % len(ports)
-    vlans = call_vland('db_query', {'command':'db.all_vlans', 'data':None})
-    print '  DB knows about %d vlan(s)' % len(vlans)
+    try:
+        print 'Config:'
+        print '  knows about %d switch(es)' % len(config.switches)
+        default_vlan_id = call_vland('db_query',
+                                     {'command':'db.get_vlan_id_by_tag',
+                                      'data':
+                                     {'tag': config.vland.default_vlan_tag}})
+        print 'The default vlan tag (%d) is vlan ID %d' % (config.vland.default_vlan_tag, default_vlan_id)
+        stat = call_vland('daemon_query', {'command':'daemon.status', 'data': None})
+        print 'VLANd is running %s' % stat['running']
+        lastmod = datetime.datetime.strptime(stat['last_modified'], '%Y-%m-%dT%H:%M:%S.%f')
+        print 'DB Last modified %s' % lastmod.strftime('%Y-%m-%d %H:%M:%S %Z')
+        print 'DB via VLANd:'
+        switches = call_vland('db_query', {'command':'db.all_switches', 'data':None})
+        print '  knows about %d switch(es)' % len(switches)
+        ports = call_vland('db_query', {'command':'db.all_ports', 'data':None})
+        print '  knows about %d port(s)' % len(ports)
+        vlans = call_vland('db_query', {'command':'db.all_vlans', 'data':None})
+        print '  DB knows about %d vlan(s)' % len(vlans)
+    except:
+        exit = Error.FAILED
 elif args.which == 'shutdown':
-    print 'Asking VLANd to shutdown'
-    shutdown = call_vland('daemon_query',
-                          {'command':'daemon.shutdown',
-                           'data': None})
-    for field in shutdown:
-        print '%s: %s' % (field, shutdown[field])
+    try:
+        print 'Asking VLANd to shutdown'
+        shutdown = call_vland('daemon_query',
+                            {'command':'daemon.shutdown',
+                            'data': None})
+        for field in shutdown:
+            print '%s: %s' % (field, shutdown[field])
+    except:
+        exit = Error.FAILED                
 elif args.which == 'vland_version':
-    ver = call_vland('daemon_query', {'command':'daemon.version', 'data': None})
-    print 'VLANd version %s' % ver['version']
+    try:
+        ver = call_vland('daemon_query', {'command':'daemon.version', 'data': None})
+        print 'VLANd version %s' % ver['version']
+    except:
+        exit = Error.FAILED        
 elif args.which == 'statistics':
-    stats = call_vland('daemon_query', {'command':'daemon.statistics', 'data': None})
-    print 'VLANd uptime: %d seconds' % stats['uptime']
+    try:
+        stats = call_vland('daemon_query', {'command':'daemon.statistics', 'data': None})
+        print 'VLANd uptime: %d seconds' % stats['uptime']
+    except:
+        exit = Error.FAILED        
 elif args.which == 'version':
     print 'VLANd admin interface version %s' % version
 elif args.which == 'auto_import_switch':
     print 'Attempting to import switch %s' % args.name
     if args.name not in config.switches:
         raise InputError("Can't find switch %s in config" % args.name)
-    imp = call_vland('vlan_update',
-                     {'command':'api.auto_import_switch',
-                      'data':
-                      {'switch': args.name}})
-    print 'VLANd imported switch %s successfully: new switch_id %d, %d new ports, %d new VLANs' % (args.name, imp['switch_id'], imp['num_ports_added'], imp['num_vlans_added'])
+    try:
+        imp = call_vland('vlan_update',
+                        {'command':'api.auto_import_switch',
+                        'data':
+                        {'switch': args.name}})
+        print 'VLANd imported switch %s successfully: new switch_id %d, %d new ports, %d new VLANs' % (args.name, imp['switch_id'], imp['num_ports_added'], imp['num_vlans_added'])
+    except:
+        print 'Import failed - see log for details'
+        exit = Error.FAILED
 elif args.which == 'probe_switches':
     print 'Asking VLANd to probe all the configured switches'
-    probe = call_vland('daemon_query',
-                       {'command':'daemon.probe_switches',
-                        'data': None})
-    for field in probe:
-        print '%s: %s' % (field, probe[field])
+    try:
+        probe = call_vland('daemon_query',
+                            {'command':'daemon.probe_switches',
+                            'data': None})
+        for field in probe:
+            print '%s: %s' % (field, probe[field])
+    except:
+        print 'Probe failed - see log for details'
+        exit = Error.FAILED
 elif args.which == 'list_all_switches':
-    result = call_vland('db_query', {'command':'db.all_switches', 'data':None})
-    for line in result:
-        dump_switch(line)
+    try:
+        result = call_vland('db_query', {'command':'db.all_switches', 'data':None})
+        for line in result:
+            dump_switch(line)
+    except:
+        exit = Error.FAILED        
 elif args.which == 'list_all_ports':
-    result = call_vland('db_query', {'command':'db.all_ports', 'data':None})
-    for line in result:
-        dump_port(line)
+    try:
+        result = call_vland('db_query', {'command':'db.all_ports', 'data':None})
+        for line in result:
+            dump_port(line)
+    except:
+        exit = Error.FAILED
 elif args.which == 'list_all_vlans':
-    result = call_vland('db_query', {'command':'db.all_vlans', 'data':None})
-    for line in result:
-        dump_vlan(line)
+    try:
+        result = call_vland('db_query', {'command':'db.all_vlans', 'data':None})
+        for line in result:
+            dump_vlan(line)
+    except:
+        exit = Error.FAILED        
 elif args.which == 'list_all_trunks':
-    result = call_vland('db_query', {'command':'db.all_trunks', 'data':None})
-    for line in result:
-        dump_trunk(line)
+    try:
+        result = call_vland('db_query', {'command':'db.all_trunks', 'data':None})
+        for line in result:
+            dump_trunk(line)
+    except:
+        exit = Error.FAILED        
 elif args.which == 'create_switch':
     try:
         switch_id = call_vland('db_update',
@@ -440,6 +473,7 @@
         print 'Created switch_id %d' % switch_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
 elif args.which == 'create_port':
     try:
         port_id = call_vland('db_update',
@@ -451,6 +485,10 @@
         print 'Created port_id %d' % port_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'create_vlan':
     try:
         (vlan_id, vlan_tag) = call_vland('vlan_update',
@@ -462,6 +500,10 @@
         print 'Created VLAN tag %d as vlan_id %d' % (vlan_tag, vlan_id)
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'create_trunk':
     try:
         trunk_id = call_vland('db_update',
@@ -472,6 +514,10 @@
         print 'Created trunk_id %d' % trunk_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'delete_switch':
     try:
         switch_id = call_vland('db_update',
@@ -480,6 +526,10 @@
         print 'Deleted switch_id %s' % switch_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'delete_port':
     try:
         port_id = call_vland('db_update',
@@ -488,6 +538,10 @@
         print 'Deleted port_id %s' % port_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'delete_vlan':
     try:
         vlan_id = call_vland('vlan_update',
@@ -496,6 +550,10 @@
         print 'Deleted vlan_id %d' % vlan_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'delete_trunk':
     try:
         port_id = call_vland('db_update',
@@ -504,6 +562,10 @@
         print 'Deleted trunk_id %s' % trunk_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'lookup_switch_by_name':
     try:
         switch_id = call_vland('db_query',
@@ -513,8 +575,10 @@
             print '%d' % switch_id
         else:
             print 'No switch found for name %s' % args.name
+            exit = Error.NOTFOUND
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
 elif args.which == 'show_switch':
     try:
         this_switch = call_vland('db_query',
@@ -527,6 +591,7 @@
             print 'No switch found for switch_id %s' % args.switch_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
 elif args.which == 'show_port':
     try:
         this_port = call_vland('db_query',
@@ -537,8 +602,10 @@
             dump_port(this_port)
         else:
             print 'No port found for port_id %s' % args.port_id
+            exit = Error.NOTFOUND
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
 elif args.which == 'lookup_port_by_switch_and_name':
     try:
         p = call_vland('db_query',
@@ -550,6 +617,7 @@
             print p
         else:
             print 'No port found for switch_id %s, name %s' % (args.switch_id, args.name)
+            exit = Error.NOTFOUND
     except InputError as inst:
         print 'Failed: %s' % inst
 elif args.which == 'lookup_port_by_switch_and_number':
@@ -565,6 +633,7 @@
             print 'No port found for switch_id %s, port number %s' % (args.switch_id, args.number)
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
 elif args.which == 'lookup_ports_by_switch':
     try:
         p = call_vland('db_query',
@@ -576,8 +645,10 @@
                 print port_id
         else:
             print 'No ports found for switch_id %s' % args.switch_id
+            exit = Error.NOTFOUND
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
 elif args.which == 'lookup_ports_by_current_vlan':
     try:
         p = call_vland('db_query',
@@ -589,8 +660,10 @@
                 print port_id
         else:
             print 'No ports found for current vlan_id %s' % args.vlan_id
+            exit = Error.NOTFOUND
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
 elif args.which == 'lookup_ports_by_base_vlan':
     try:
         p = call_vland('db_query',
@@ -602,8 +675,10 @@
                 print port_id
         else:
             print 'No ports found for base vlan_id %s' % args.vlan_id
+            exit = Error.NOTFOUND
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
 elif args.which == 'lookup_ports_by_trunk':
     try:
         p = call_vland('db_query',
@@ -615,8 +690,10 @@
                 print port_id
         else:
             print 'No ports found for trunk_id %s' % args.trunk_id
+            exit = Error.NOTFOUND
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
 elif args.which == 'set_port_mode':
     try:
         port_id = call_vland('vlan_update',
@@ -627,6 +704,10 @@
         print "Updated mode for port_id %d" % port_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'lock_port':
     try:
         port_id = call_vland('db_update',
@@ -637,6 +718,10 @@
         print "Locked port_id %d" % port_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'unlock_port':
     try:
         port_id = call_vland('db_update',
@@ -647,6 +732,10 @@
         print "Unlocked port_id %d" % port_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'set_port_current_vlan':
     try:
         port_id = call_vland('vlan_update',
@@ -657,6 +746,10 @@
         print "Set current VLAN on port_id %d" % port_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'get_port_current_vlan':
     try:
         vlan_id = call_vland('db_query',
@@ -669,6 +762,10 @@
             print "No current_vlan_id found for port_id %s" % args.port_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'set_port_base_vlan':
     try:
         port_id = call_vland('db_update',
@@ -679,6 +776,10 @@
         print "Set base VLAN on port_id %d" % port_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'get_port_base_vlan':
     try:
         vlan_id = call_vland('db_query',
@@ -691,6 +792,10 @@
             print "No base_vlan_id found for port_id %d" % port_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'restore_port_to_base_vlan':
     try:
         port_id = call_vland('vlan_update',
@@ -700,6 +805,10 @@
         print "Restored port_id %d back to base VLAN" % port_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'show_vlan':
     try:
         v = call_vland('db_query',
@@ -712,6 +821,10 @@
             print 'No VLAN found for vlan_id %s' % args.vlan_id
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 elif args.which == 'lookup_vlan_by_tag':
     try:
         vlan_id = call_vland('db_query',
@@ -722,8 +835,10 @@
             print vlan_id
         else:
             print 'No VLAN found for vlan tag %s' % args.tag
+            exit = Error.NOTFOUND
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
 elif args.which == 'show_vlan_tag':
     try:
         vlan_tag = call_vland('db_query',
@@ -734,8 +849,10 @@
             print vlan_tag
         else:
             print 'No VLAN found for vlan id %s' % args.vlan_id
+            exit = Error.NOTFOUND
     except InputError as inst:
         print 'Failed: %s' % inst
+        exit = Error.FAILED
 elif args.which == 'show_trunk':
     try:
         this_trunk = call_vland('db_query',
@@ -748,6 +865,12 @@
             print 'No port found for port_id %s' % args.trunk_id
     except InputError as inst:
         print 'Failed: %s' % inst
-
+        print 'Failed: %s' % inst
+        exit = Error.FAILED
+    except NotFoundError as inst:
+        print 'Failed: %s' % inst
+        exit = Error.NOTFOUND
 else:
     print 'No recognised command given. Try -h for help'
+
+sys.exit(exit)