#! /usr/bin/python

#  Copyright 2014-2018 Linaro Limited
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#
#  VLANd admin interface
#

import os, sys
import argparse
import datetime, time

from Vland.errors import InputError, SocketError, NotFoundError, Error
from Vland.config.config import VlanConfig
from Vland.ipc.ipc import VlanIpc

prog = "vland-admin"
version = "0.8"
banner = "Linaro VLANd admin interface, version %s" % version
exitcode = Error.OK
TRUNK_ID_NONE = -1
config_file_list = ['/etc/vland.cfg',
                    './vland.cfg',
                    os.path.expanduser('~/.vland.cfg')]

def is_positive(text):
    if text in ('1', 'y', 'Y', 't', 'T', 'True', 'true'):
        return True
    elif text in ('0', 'n', 'N', 'f', 'F', 'False', 'false'):
        return False
    else:
        raise InputError("Cannot parse \"%s\" as True or False" % text)

def dump_switch(switch):
    print "switch_id:%d name:%s" % (
        int(switch['switch_id']),
        switch['name'])

def dump_port(port):
    print "port_id:%d name:%s switch_id:%d locked:%s mode:%s base_vlan_id:%d current_vlan_id:%d number:%d trunk_id:%s lock_reason:%s" % (
        int(port['port_id']),
        port['name'],
        int(port['switch_id']),
        ("yes" if port['is_locked'] is True else "no"),
        ("trunk" if port['is_trunk'] is True else "access"),
        int(port['base_vlan_id']),
        int(port['current_vlan_id']),
        int(port['number']),
        'None' if (TRUNK_ID_NONE == port['trunk_id']) else port['trunk_id'],
        port['lock_reason'])

def dump_vlan(vlan):
    print "vlan_id:%d name:%s tag:%d is_base_vlan:%s, creation_time:%s" % (
        int(vlan['vlan_id']),
        vlan['name'],
        int(vlan['tag']),
        ("yes" if vlan['is_base_vlan'] is True else "no"),
        vlan['creation_time'])

def dump_trunk(trunk):
    print "trunk_id:%d creation_time:%s" % (
        int(trunk['trunk_id']),
        trunk['creation_time'])

def call_vland(msgtype, msg):
    ipc = VlanIpc()
    ipc.client_connect('localhost', config.vland.port)
    msg['client_name'] = 'vland-admin'
    msg['type'] = msgtype
    ipc.client_send(msg)
    ret = ipc.client_recv_and_close()
    if 'response' not in ret:
        raise SocketError("Badly-formed response from VLANd server")
    if ret['response'] == "ERROR":
        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=config_file_list)

parser = argparse.ArgumentParser()

####################
# System commands
####################
sp = parser.add_subparsers(title='Sub-commands',
                help="Commands",
                dest='subparser_name')
p_status = sp.add_parser("status",
                help="Describe current system status")
p_status.set_defaults(which = "status")
p_statistics = sp.add_parser("statistics",
                help="Print some system statistics")
p_statistics.set_defaults(which = "statistics")
p_version = sp.add_parser("version",
                help="Describe the version of this admin interface")
p_version.set_defaults(which = "version")
p_vland_version = sp.add_parser("vland_version",
                help="Describe the version of the running VLANd")
p_vland_version.set_defaults(which = "vland_version")
p_probe_switches = sp.add_parser("probe_switches",
                help="Probe all the configured switches")
p_probe_switches.set_defaults(which = "probe_switches")
p_auto_import = sp.add_parser("auto_import_switch",
                help="Attempt to import a switch's configuration into the VLANd system")
p_auto_import.add_argument('--name',
                required = True,
                help="Import the switch named SWITCH_NAME in vland.cfg")
p_auto_import.set_defaults(which = "auto_import_switch")
p_shutdown = sp.add_parser("shutdown",
                help="Shut down a running VLANd")
p_shutdown.set_defaults(which = "shutdown")

####################
# Switch commands
####################
p_list_all_switches = sp.add_parser("list_all_switches",
                help="List all the existing switches in the system")
p_list_all_switches.set_defaults(which = "list_all_switches")
p_create_switch = sp.add_parser("create_switch",
                help = "Add a new switch to the system")
p_create_switch.set_defaults(which = "create_switch")
p_create_switch.add_argument('--name',
                required = True,
                help="The name of the new switch, as described in vland.cfg")
p_delete_switch = sp.add_parser("delete_switch",
                help = "Remove an existing switch from the system")
p_delete_switch.set_defaults(which = "delete_switch")
p_delete_switch.add_argument('--switch_id',
                required = True,
                help = "The ID of the switch to remove")
p_show_switch = sp.add_parser("show_switch",
                help = "Show the details of an existing switch in the system")
p_show_switch.set_defaults(which = "show_switch")
p_show_switch.add_argument('--switch_id',
                required = True,
                help = "The ID of the switch to show")
p_lookup_switch_by_name = sp.add_parser("lookup_switch_by_name",
                help = "Lookup a switch ID by name")
p_lookup_switch_by_name.set_defaults(which = "lookup_switch_by_name")
p_lookup_switch_by_name.add_argument('--name',
                required = True,
                help = "The switch name to search for")

####################
# Ports commands
####################
p_list_all_ports = sp.add_parser("list_all_ports",
                help="List all the existing ports in the system")
p_list_all_ports.set_defaults(which = "list_all_ports")
p_create_port = sp.add_parser("create_port",
                help = "Add a new port to the system")
p_create_port.set_defaults(which = "create_port")
p_create_port.add_argument('--switch_id',
                required = True,
                help="The ID of the switch containing the new port")
p_create_port.add_argument('--name',
                required = True,
                help="The name of the new port")
p_create_port.add_argument('--number',
                required = True,
                help="The human-friendly number for the new port")
p_delete_port = sp.add_parser("delete_port",
                help = "Remove an existing port from the system")
p_delete_port.set_defaults(which = "delete_port")
p_delete_port.add_argument('--port_id',
                required = True,
                help = "The ID of the port to remove")
p_show_port = sp.add_parser("show_port",
                help = "Show the details of an existing port in the system")
p_show_port.set_defaults(which = "show_port")
p_show_port.add_argument('--port_id',
                required = True,
                help = "The ID of the port to show")
p_lookup_port_by_switch_and_name = sp.add_parser("lookup_port_by_switch_and_name",
                help = "Lookup a port ID by switch ID and port name")
p_lookup_port_by_switch_and_name.set_defaults(which = "lookup_port_by_switch_and_name")
p_lookup_port_by_switch_and_name.add_argument('--switch_id',
                required = True,
                help = "The switch ID to search for")
p_lookup_port_by_switch_and_name.add_argument('--name',
                required = True,
                help = "The port name to search for")
p_lookup_port_by_switch_and_number = sp.add_parser("lookup_port_by_switch_and_number",
                help = "Lookup a port ID by switch ID and port number")
p_lookup_port_by_switch_and_number.set_defaults(which = "lookup_port_by_switch_and_number")
p_lookup_port_by_switch_and_number.add_argument('--switch_id',
                required = True,
                help = "The switch ID to search for")
p_lookup_port_by_switch_and_number.add_argument('--number',
                required = True,
                help = "The port number to search for")
p_lookup_ports_by_switch = sp.add_parser("lookup_ports_by_switch",
                help = "Lookup port ID(s) by switch ID")
p_lookup_ports_by_switch.set_defaults(which = "lookup_ports_by_switch")
p_lookup_ports_by_switch.add_argument('--switch_id',
                required = True,
                help = "The switch ID to search for")
p_lookup_ports_by_current_vlan = sp.add_parser("lookup_ports_by_current_vlan",
                help = "Lookup port ID(s) by current VLAN ID")
p_lookup_ports_by_current_vlan.set_defaults(which = "lookup_ports_by_current_vlan")
p_lookup_ports_by_current_vlan.add_argument('--vlan_id',
                required = True,
                help = "The VLAN ID to search for")
p_lookup_ports_by_base_vlan = sp.add_parser("lookup_ports_by_base_vlan",
                help = "Lookup port ID(s) by base vlan ID")
p_lookup_ports_by_base_vlan.set_defaults(which = "lookup_ports_by_base_vlan")
p_lookup_ports_by_base_vlan.add_argument('--vlan_id',
                required = True,
                help = "The VLAN ID to search for")
p_lookup_ports_by_trunk = sp.add_parser("lookup_ports_by_trunk",
                help = "Lookup port ID(s) by trunk ID")
p_lookup_ports_by_trunk.set_defaults(which = "lookup_ports_by_trunk")
p_lookup_ports_by_trunk.add_argument('--trunk_id',
                required = True,
                help = "The trunk ID to search for")
p_set_port_mode = sp.add_parser("set_port_mode",
                help = "Set the mode of a port to 'trunk' or 'access'")
p_set_port_mode.set_defaults(which = "set_port_mode")
p_set_port_mode.add_argument('--port_id',
                required = True,
                help = "The ID of the port to modify")
p_set_port_mode.add_argument('--mode',
                required = True,
                help = "The mode to select ('trunk' or 'access')")
p_get_port_mode = sp.add_parser("get_port_mode",
                help = "Get the mode of a port ('trunk' or 'access')")
p_get_port_mode.set_defaults(which = "get_port_mode")
p_get_port_mode.add_argument('--port_id',
                required = True,
                help = "The ID of the port to query")
p_lock_port = sp.add_parser("lock_port",
                help = "Lock the settings on a port")
p_lock_port.set_defaults(which = "lock_port")
p_lock_port.add_argument('--port_id',
                required = True,
                help = "The ID of the port to lock")
p_lock_port.add_argument('--reason',
                required = False,
                help = "(Optional) The reason for locking the port")
p_unlock_port = sp.add_parser("unlock_port",
                help = "Unlock the settings on a port")
p_unlock_port.set_defaults(which = "unlock_port")
p_unlock_port.add_argument('--port_id',
                required = True,
                help = "The ID of the port to unlock")
p_set_port_current_vlan = sp.add_parser("set_port_current_vlan",
                help = "Set the current VLAN assignment for a port")
p_set_port_current_vlan.set_defaults(which = "set_port_current_vlan")
p_set_port_current_vlan.add_argument('--port_id',
                required = True,
                help = "The ID of the port to modify")
p_set_port_current_vlan.add_argument('--vlan_id',
                required = True,
                help = "The VLAN ID to be used")
p_get_port_current_vlan = sp.add_parser("get_port_current_vlan",
                help = "Get the current VLAN assignment for a port")
p_get_port_current_vlan.set_defaults(which = "get_port_current_vlan")
p_get_port_current_vlan.add_argument('--port_id',
                required = True,
                help = "The ID of the port to query")
p_set_port_base_vlan = sp.add_parser("set_port_base_vlan",
                help = "Set the base VLAN assignment for a port")
p_set_port_base_vlan.set_defaults(which = "set_port_base_vlan")
p_set_port_base_vlan.add_argument('--port_id',
                required = True,
                help = "The ID of the port to modify")
p_set_port_base_vlan.add_argument('--vlan_id',
                required = True,
                help = "The VLAN ID to be used")
p_get_port_base_vlan = sp.add_parser("get_port_base_vlan",
                help = "Get the base VLAN assignment for a port")
p_get_port_base_vlan.set_defaults(which = "get_port_base_vlan")
p_get_port_base_vlan.add_argument('--port_id',
                required = True,
                help = "The ID of the port to query")
p_restore_port_to_base_vlan = sp.add_parser("restore_port_to_base_vlan",
                help = "Reset a port back to its base VLAN")
p_restore_port_to_base_vlan.set_defaults(which = "restore_port_to_base_vlan")
p_restore_port_to_base_vlan.add_argument('--port_id',
                required = True,
                help = "The ID of the port to modify")

####################
# VLAN commands
####################
p_list_all_vlans = sp.add_parser("list_all_vlans",
                help="List all the existing VLANs in the system")
p_list_all_vlans.set_defaults(which = "list_all_vlans")
p_create_vlan = sp.add_parser("create_vlan",
                help = "Add a new VLAN to the system")
p_create_vlan.set_defaults(which = "create_vlan")
p_create_vlan.add_argument('--name',
                required = True,
                help="The name of the new VLAN")
p_create_vlan.add_argument('--tag',
                required = True,
                help="The tag for the new VLAN (-1 to automatically select)")
p_create_vlan.add_argument('--is_base_vlan',
                required = True,
                help="Is the new VLAN a base VLAN (true/false)")
p_delete_vlan = sp.add_parser("delete_vlan",
                help = "Remove an existing VLAN from the system")
p_delete_vlan.set_defaults(which = "delete_vlan")
p_delete_vlan.add_argument('--vlan_id',
                required = True,
                help = "The ID of the VLAN to remove")
p_show_vlan = sp.add_parser("show_vlan",
                help = "Show the details of an existing VLAN in the system")
p_show_vlan.set_defaults(which = "show_vlan")
p_show_vlan.add_argument('--vlan_id',
                required = True,
                help = "The ID of the VLAN to show")
p_lookup_vlan_by_tag = sp.add_parser("lookup_vlan_by_tag",
                help = "Find the VLAN ID of an existing VLAN in the system")
p_lookup_vlan_by_tag.set_defaults(which = "lookup_vlan_by_tag")
p_lookup_vlan_by_tag.add_argument('--tag',
                required = True,
                help = "The VLAN tag to search for")
p_show_vlan_tag = sp.add_parser("show_vlan_tag",
                help = "Print the VLAN tag of an existing VLAN in the system")
p_show_vlan_tag.set_defaults(which = "show_vlan_tag")
p_show_vlan_tag.add_argument('--vlan_id',
                required = True,
                help = "The VLAN ID to search for")

####################
# Trunk commands
####################
p_list_all_trunks = sp.add_parser("list_all_trunks",
                help="List all the existing trunks in the system")
p_list_all_trunks.set_defaults(which = "list_all_trunks")
p_create_trunk = sp.add_parser("create_trunk",
                help = "Add a new trunk to the system, linking two ports")
p_create_trunk.set_defaults(which = "create_trunk")
p_create_trunk.add_argument('--port_id1',
                required = True,
                help="The ID of the first port to be used")
p_create_trunk.add_argument('--port_id2',
                required = True,
                help="The ID of the second port to be used")
p_delete_trunk = sp.add_parser("delete_trunk",
                help = "Remove an existing trunk from the system")
p_delete_trunk.set_defaults(which = "delete_trunk")
p_delete_trunk.add_argument('--trunk_id',
                required = True,
                help = "The ID of the trunk to remove")
p_show_trunk = sp.add_parser("show_trunk",
                help = "Show the details of an existing trunk in the system")
p_show_trunk.set_defaults(which = "show_trunk")
p_show_trunk.add_argument('--trunk_id',
                required = True,
                help = "The ID of the trunk to show")

args = parser.parse_args()

# Now work out what to do
if args.which == 'status':
    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 (InputError, NotFoundError):
        exitcode = Error.FAILED
elif args.which == 'shutdown':
    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 (InputError, NotFoundError):
        exitcode = Error.FAILED
elif args.which == 'vland_version':
    try:
        ver = call_vland('daemon_query', {'command':'daemon.version', 'data': None})
        print 'VLANd version %s' % ver['version']
    except (InputError, NotFoundError):
        exitcode = Error.FAILED
elif args.which == 'statistics':
    try:
        stats = call_vland('daemon_query', {'command':'daemon.statistics', 'data': None})
        print 'VLANd uptime: %d seconds' % stats['uptime']
    except (InputError, NotFoundError):
        exitcode = 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)
    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 (InputError, NotFoundError):
        print 'Import failed - see log for details'
        exitcode = Error.FAILED
elif args.which == 'probe_switches':
    print 'Asking VLANd to probe all the configured switches'
    try:
        probe = call_vland('daemon_query',
                            {'command':'daemon.probe_switches',
                            'data': None})
        for field in probe:
            print '%s: %s' % (field, probe[field])
    except (InputError, NotFoundError):
        print 'Probe failed - see log for details'
        exitcode = Error.FAILED
elif args.which == 'list_all_switches':
    try:
        result = call_vland('db_query', {'command':'db.all_switches', 'data':None})
        for line in result:
            dump_switch(line)
    except (InputError, NotFoundError):
        exitcode = Error.FAILED
elif args.which == 'list_all_ports':
    try:
        result = call_vland('db_query', {'command':'db.all_ports', 'data':None})
        for line in result:
            dump_port(line)
    except (InputError, NotFoundError):
        exitcode = Error.FAILED
elif args.which == 'list_all_vlans':
    try:
        result = call_vland('db_query', {'command':'db.all_vlans', 'data':None})
        for line in result:
            dump_vlan(line)
    except (InputError, NotFoundError):
        exitcode = Error.FAILED
elif args.which == 'list_all_trunks':
    try:
        result = call_vland('db_query', {'command':'db.all_trunks', 'data':None})
        for line in result:
            dump_trunk(line)
    except (InputError, NotFoundError):
        exitcode = Error.FAILED
elif args.which == 'create_switch':
    try:
        switch_id = call_vland('db_update',
                               {'command':'db.create_switch',
                                'data':
                                {'name':args.name}})
        print 'Created switch_id %d' % switch_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
elif args.which == 'create_port':
    try:
        port_id = call_vland('db_update',
                             {'command':'db.create_port',
                              'data':
                              {'switch_id': args.switch_id,
                                'name': args.name,
                                'number': args.number}})
        print 'Created port_id %d' % port_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'create_vlan':
    try:
        (vlan_id, vlan_tag) = call_vland('vlan_update',
                                {'command':'api.create_vlan',
                                'data':
                                {'name': args.name,
                                'tag': args.tag,
                                'is_base_vlan': is_positive(args.is_base_vlan)}})
        print 'Created VLAN tag %d as vlan_id %d' % (vlan_tag, vlan_id)
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'create_trunk':
    try:
        trunk_id = call_vland('db_update',
                              {'command':'db.create_trunk',
                               'data':
                               {'port_id1': args.port_id1,
                                'port_id2': args.port_id2}})
        print 'Created trunk_id %d' % trunk_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'delete_switch':
    try:
        switch_id = call_vland('db_update',
                               {'command':'db.delete_switch',
                                'data': {'switch_id': args.switch_id}})
        print 'Deleted switch_id %s' % switch_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'delete_port':
    try:
        port_id = call_vland('db_update',
                             {'command':'db.delete_port',
                              'data': {'port_id': args.port_id}})
        print 'Deleted port_id %s' % port_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'delete_vlan':
    try:
        vlan_id = call_vland('vlan_update',
                             {'command':'api.delete_vlan',
                              'data': {'vlan_id': args.vlan_id}})
        print 'Deleted vlan_id %d' % vlan_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'delete_trunk':
    try:
        trunk_id = call_vland('db_update',
                             {'command':'db.delete_trunk',
                              'data': {'trunk_id': args.trunk_id}})
        print 'Deleted trunk_id %s' % trunk_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'lookup_switch_by_name':
    try:
        switch_id = call_vland('db_query',
                               {'command':'db.get_switch_id_by_name',
                                'data':{'name':args.name}})
        if switch_id is not None:
            print '%d' % switch_id
        else:
            print 'No switch found for name %s' % args.name
            exitcode = Error.NOTFOUND
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
elif args.which == 'show_switch':
    try:
        this_switch = call_vland('db_query',
                                 {'command':'db.get_switch_by_id',
                                  'data':
                                  {'switch_id': args.switch_id}})
        if this_switch is not None:
            dump_switch(this_switch)
        else:
            print 'No switch found for switch_id %s' % args.switch_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
elif args.which == 'show_port':
    try:
        this_port = call_vland('db_query',
                               {'command':'db.get_port_by_id',
                                'data':
                                {'port_id': args.port_id}})
        if this_port is not None:
            dump_port(this_port)
        else:
            print 'No port found for port_id %s' % args.port_id
            exitcode = Error.NOTFOUND
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
elif args.which == 'lookup_port_by_switch_and_name':
    try:
        p = call_vland('db_query',
                       {'command':'db.get_port_by_switch_and_name',
                        'data':
                        {'switch_id': args.switch_id,
                         'name': args.name}})
        if p is not None:
            print p
        else:
            print 'No port found for switch_id %s, name %s' % (args.switch_id, args.name)
            exitcode = Error.NOTFOUND
    except InputError as inst:
        print 'Failed: %s' % inst
elif args.which == 'lookup_port_by_switch_and_number':
    try:
        p = call_vland('db_query',
                       {'command':'db.get_port_by_switch_and_number',
                        'data':
                        {'switch_id': args.switch_id,
                         'number': args.number}})
        if p is not None:
            print p
        else:
            print 'No port found for switch_id %s, port number %s' % (args.switch_id, args.number)
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
elif args.which == 'lookup_ports_by_switch':
    try:
        p = call_vland('db_query',
                       {'command':'db.get_ports_by_switch',
                        'data':
                        {'switch_id': args.switch_id}})
        if p is not None:
            for port_id in p:
                print port_id
        else:
            print 'No ports found for switch_id %s' % args.switch_id
            exitcode = Error.NOTFOUND
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
elif args.which == 'lookup_ports_by_current_vlan':
    try:
        p = call_vland('db_query',
                       {'command':'db.get_ports_by_current_vlan',
                        'data':
                        {'vlan_id': args.vlan_id}})
        if p is not None:
            for port_id in p:
                print port_id
        else:
            print 'No ports found for current vlan_id %s' % args.vlan_id
            exitcode = Error.NOTFOUND
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
elif args.which == 'lookup_ports_by_base_vlan':
    try:
        p = call_vland('db_query',
                       {'command':'db.get_ports_by_base_vlan',
                        'data':
                        {'vlan_id': args.vlan_id}})
        if p is not None:
            for port_id in p:
                print port_id
        else:
            print 'No ports found for base vlan_id %s' % args.vlan_id
            exitcode = Error.NOTFOUND
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
elif args.which == 'lookup_ports_by_trunk':
    try:
        p = call_vland('db_query',
                       {'command':'db.get_ports_by_trunk',
                        'data':
                        {'trunk_id': args.trunk_id}})
        if p is not None:
            for port_id in p:
                print port_id
        else:
            print 'No ports found for trunk_id %s' % args.trunk_id
            exitcode = Error.NOTFOUND
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
elif args.which == 'get_port_mode':
    try:
        mode = call_vland('db_query',
                          {'command':'db.get_port_mode',
                           'data':
                           {'port_id': args.port_id}})
        if mode is not None:
            print 'port_id %s is in mode %s' % (args.port_id, mode)
        else:
            print 'No port found for port_id %s' % args.port_id
            exitcode = Error.NOTFOUND
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'set_port_mode':
    try:
        port_id = call_vland('vlan_update',
                             {'command':'api.set_port_mode',
                              'data':
                              {'port_id': args.port_id,
                               'mode': args.mode}})
        print "Updated mode for port_id %d" % port_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'lock_port':
    try:
        port_id = call_vland('db_update',
                             {'command':'db.set_port_is_locked',
                              'data':
                              {'port_id': args.port_id,
                               'is_locked': True,
                               'lock_reason': args.reason}})
        print "Locked port_id %d" % port_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'unlock_port':
    try:
        port_id = call_vland('db_update',
                             {'command':'db.set_port_is_locked',
                              'data':
                              {'port_id': args.port_id,
                               'is_locked': False,
                               'lock_reason': ""}})
        print "Unlocked port_id %d" % port_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'set_port_current_vlan':
    try:
        port_id = call_vland('vlan_update',
                             {'command':'api.set_current_vlan',
                              'data':
                              {'port_id': args.port_id,
                               'vlan_id': args.vlan_id}})
        print "Set current VLAN on port_id %d" % port_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'get_port_current_vlan':
    try:
        vlan_id = call_vland('db_query',
                             {'command':'db.get_current_vlan_id_by_port',
                              'data':
                              {'port_id': args.port_id}})
        if vlan_id is not None:
            print vlan_id
        else:
            print "No current_vlan_id found for port_id %s" % args.port_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'set_port_base_vlan':
    try:
        port_id = call_vland('db_update',
                             {'command':'db.set_base_vlan',
                              'data':
                              {'port_id': args.port_id,
                               'base_vlan_id': args.vlan_id}})
        print "Set base VLAN on port_id %d" % port_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'get_port_base_vlan':
    try:
        vlan_id = call_vland('db_query',
                             {'command':'db.get_base_vlan_id_by_port',
                              'data':
                              {'port_id': args.port_id}})
        if vlan_id is not None:
            print vlan_id
        else:
            print "No base_vlan_id found for port_id %d" % port_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'restore_port_to_base_vlan':
    try:
        port_id = call_vland('vlan_update',
                             {'command': 'api.restore_base_vlan',
                              'data':
                              {'port_id': args.port_id}})
        print "Restored port_id %d back to base VLAN" % port_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'show_vlan':
    try:
        v = call_vland('db_query',
                       {'command':'db.get_vlan_by_id',
                        'data':
                        {'vlan_id': args.vlan_id}})
        if v is not None:
            dump_vlan(v)
        else:
            print 'No VLAN found for vlan_id %s' % args.vlan_id
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
elif args.which == 'lookup_vlan_by_tag':
    try:
        vlan_id = call_vland('db_query',
                             {'command':'db.get_vlan_id_by_tag',
                              'data':
                              {'tag': args.tag}})
        if vlan_id is not None:
            print vlan_id
        else:
            print 'No VLAN found for vlan tag %s' % args.tag
            exitcode = Error.NOTFOUND
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
elif args.which == 'show_vlan_tag':
    try:
        vlan_tag = call_vland('db_query',
                              {'command':'db.get_vlan_tag_by_id',
                               'data':
                               {'vlan_id': args.vlan_id}})
        if vlan_tag is not None:
            print vlan_tag
        else:
            print 'No VLAN found for vlan id %s' % args.vlan_id
            exitcode = Error.NOTFOUND
    except InputError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
elif args.which == 'show_trunk':
    try:
        this_trunk = call_vland('db_query',
                                {'command':'db.get_trunk_by_id',
                                 'data':
                                {'trunk_id': args.trunk_id}})
        if this_trunk is not None:
            dump_trunk(this_trunk)
        else:
            print 'No port found for port_id %s' % args.trunk_id
    except InputError as inst:
        print 'Failed: %s' % inst
        print 'Failed: %s' % inst
        exitcode = Error.FAILED
    except NotFoundError as inst:
        print 'Failed: %s' % inst
        exitcode = Error.NOTFOUND
else:
    print 'No recognised command given. Try -h for help'

sys.exit(exitcode)
