#!/usr/bin/python2.7
# encoding: utf-8
#
# Copyright (c) 2013 Fizians SAS. <http://www.fizians.com>
# This file is part of Rozofs.
#
# Rozofs 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, version 2.
#
# Rozofs 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, see
# <http://www.gnu.org/licenses/>.

'''
rozo -- RozoFS command line interface
'''

import sys
import os
import traceback

from rozofs import __version__

from argparse import ArgumentParser, Namespace, Action
from rozofs.core.constants import EXPORTD_MANAGER, STORAGED_MANAGER, \
    ROZOFSMOUNT_MANAGER
from rozofs.cli.node import STR_ROLES
from rozofs.cli.exceptions import MultipleError


__all__ = []
# __version__ = 0.1
__date__ = '2013-09-14'
__updated__ = '2015-04-15'
__export_host_env_var__ = 'ROZO_EXPORT_HOSTNAME'

DEBUG = 0
TESTRUN = 0
PROFILE = 0


# def __add_command_parser(cmd_parser, command, helpmsg, dispatch, parents=[]):
#    parser = cmd_parser.add_parser(command, help=helpmsg, parents=parents)
#    parser.set_defaults(command=command)
#    parser.set_defaults(dispatch=dispatch)
#    return parser

class CLIError(Exception):
    '''Generic exception to raise and log different fatal errors.'''
    def __init__(self, msg):
        super(CLIError).__init__(type(self))
        self.msg = "E: %s" % msg
    def __str__(self):
        return self.msg
    def __unicode__(self):
        return self.msg

class GetEnvValueAction(Action):
    def __init__(self, var_name, **kwargs):
        assert kwargs.get("required")

        var_env_value = os.environ.get(var_name)

        if var_env_value:
            kwargs["required"] = False
            kwargs["default"] = var_env_value.split(',')

        Action.__init__(self, **kwargs)

    def __call__(self, parser, namespace, values, option_string):
        setattr(namespace, self.dest, values)

def main(argv=None):  # IGNORE:C0111
    '''RozoFS command line interface.'''

    if argv is None:
        argv = sys.argv
    else:
        sys.argv.extend(argv)

    program_name = os.path.basename(sys.argv[0])
    program_version = "v%s" % __version__
    program_build_date = str(__updated__)
    program_version_message = '%%(prog)s %s (%s)' % (program_version, program_build_date)
    # program_shortdesc = __import__('__main__').__doc__.split("\n")[1]
    program_desc = "rozo -- RozoFS comand line interface (%s - %s)" % (program_version, program_build_date)
    export_help_msg = "running platform agent host (be sure to provide virtual ip if used)."

    try:
        #
        # Global options
        #

        # We use a global parser just to be able to have rozo -h without args
        global_parser = ArgumentParser(add_help=False)
        global_parser.add_argument('-V', '--version', action='version', version=program_version_message)
        global_parser.add_argument('-d', '--debug', action='store_true', default=False, help='set debugging on')
        global_parser.add_argument('-h', '--help', action='store_true', default=False, help='get help')
        global_parser.add_argument('--verbose', action='store_true', default=False, help='get command status output')

        #
        # Topics
        #
        topic_parser = ArgumentParser(description=program_desc, add_help=False, parents=[global_parser])

        topic_subparsers = topic_parser.add_subparsers(help='available topics', dest='topic')

        topic_agent_parser = topic_subparsers.add_parser('agent', help='agent commands', add_help=False)

        topic_exportd_parser = topic_subparsers.add_parser('exportd', help='exportd commands', add_help=False)
        topic_exportd_parser.add_argument('-E', '--exportd', nargs='+', action = GetEnvValueAction, var_name = __export_host_env_var__, metavar = "HOST", required = True, help = export_help_msg)

        topic_node_parser = topic_subparsers.add_parser('node', help='node commands', add_help=False)
        topic_node_parser.add_argument('-E', '--exportd', nargs='+', action = GetEnvValueAction, var_name = __export_host_env_var__, metavar = "HOST", required = True, help = export_help_msg)

        topic_volume_parser = topic_subparsers.add_parser('volume', help='volume commands', add_help=False)
        topic_volume_parser.add_argument('-E', '--exportd', nargs='+', action = GetEnvValueAction, var_name = __export_host_env_var__, metavar = "HOST", required = True, help = export_help_msg)

        topic_export_parser = topic_subparsers.add_parser('export', help='export commands', add_help=False)
        topic_export_parser.add_argument('-E', '--exportd', nargs='+', action = GetEnvValueAction, var_name = __export_host_env_var__, metavar = "HOST", required = True, help = export_help_msg)

        topic_mount_parser = topic_subparsers.add_parser('mount', help='mount commands', add_help=False)
        topic_mount_parser.add_argument('-E', '--exportd', nargs='+', action = GetEnvValueAction, var_name = __export_host_env_var__, metavar = "HOST", required = True, help = export_help_msg)

        topic_storaged_parser = topic_subparsers.add_parser('storaged', help='storaged commands', add_help=False)
        topic_storaged_parser.add_argument('-E', '--exportd', nargs='+', action = GetEnvValueAction, var_name = __export_host_env_var__, metavar = "HOST", required = True, help = export_help_msg)
        #
        # Actions
        #

        # Agent
        action_agent_parser = ArgumentParser(add_help=False, prog='rozo agent')

        action_agent_subparsers = action_agent_parser.add_subparsers(help='agent available actions', dest='action')

        action_agent_status_parser = action_agent_subparsers.add_parser('status', add_help=False, help='get agent status')

        action_agent_start_parser = action_agent_subparsers.add_parser('start', add_help=False, help='start agent')
        action_agent_start_parser.add_argument('-p', '--pacemaker', nargs='?', const='exportd_rozofs', help='exportd cluster resource name when exportd is managed thru pacemaker (default: exportd_rozofs).')
        action_agent_start_parser.add_argument('-l', '--listeners', nargs='*', choices=[[], EXPORTD_MANAGER, STORAGED_MANAGER, ROZOFSMOUNT_MANAGER], default=[], help='list of listeners.')

        action_agent_stop_parser = action_agent_subparsers.add_parser('stop', add_help=False, help='stop agent')

        action_agent_restart_parser = action_agent_subparsers.add_parser('restart', add_help=False, help='restart agent')
        action_agent_restart_parser.add_argument('-p', '--pacemaker', nargs='?', const='exportd_rozofs', help='exportd cluster resource name when exportd is managed thru pacemaker (default: exportd_rozofs).')
        action_agent_restart_parser.add_argument('-l', '--listeners', nargs='*', choices=[[], EXPORTD_MANAGER, STORAGED_MANAGER, ROZOFSMOUNT_MANAGER], default=[], help='list of listeners.')

        # Node
        action_node_parser = ArgumentParser(add_help=False, prog='rozo node')
        action_node_subparsers = action_node_parser.add_subparsers(help='node available actions', dest='action')

        action_node_list_parser = action_node_subparsers.add_parser('list', add_help=False, help='list nodes roles')
        action_node_list_parser.add_argument('-r', '--roles', nargs='+', choices=STR_ROLES.keys(), help='list of role(s) to display. If not set all roles will be displayed')

        action_node_status_parser = action_node_subparsers.add_parser('status', add_help=False, help='get node(s) status')
        action_node_status_parser.add_argument('-r', '--roles', nargs='+', choices=STR_ROLES.keys(), help='list of roles. If not set all roles.')
        action_node_status_parser.add_argument('-n', '--nodes', nargs='+', help='list of nodes. If not set all nodes.')

        action_node_config_parser = action_node_subparsers.add_parser('config', add_help=False, help='display the configuration files of the given nodes.')
        action_node_config_parser.add_argument('-n', '--nodes', nargs='+', help='list of nodes to be displayed. If not set all nodes will be displayed')
        action_node_config_parser.add_argument('-r', '--roles', nargs='+', choices=STR_ROLES.keys(), help='list of roles to be displayed for each node. If not set all roles will be displayed')

        action_node_start_parser = action_node_subparsers.add_parser('start', add_help=False, help='start node(s)')
        action_node_start_parser.add_argument('-r', '--roles', nargs='+', choices=STR_ROLES.keys(), help='list of roles. If not set all roles.')
        action_node_start_parser.add_argument('-n', '--nodes', nargs='+', help='list of nodes. If not set all nodes.')

        action_node_stop_parser = action_node_subparsers.add_parser('stop', add_help=False, help='stop node(s)')
        action_node_stop_parser.add_argument('-r', '--roles', nargs='+', choices=STR_ROLES.keys(), help='list of roles. If not set all roles.')
        action_node_stop_parser.add_argument('-n', '--nodes', nargs='+', help='list of nodes. If not set all nodes.')

        # Volume
        action_volume_parser = ArgumentParser(add_help=False, prog='rozo volume')
        action_volume_subparsers = action_volume_parser.add_subparsers(help='volume available actions', dest='action')
        action_volume_list_parser = action_volume_subparsers.add_parser('list', add_help=False, help='list volumes')

        action_volume_stat_parser = action_volume_subparsers.add_parser('stat', add_help=False, help='list volumes')

        action_volume_get_parser = action_volume_subparsers.add_parser('get', add_help=False, help='get the given volume statistics')
        action_volume_get_parser.add_argument('vid', nargs='+', type=int, help='vid to get.')

        action_volume_expand_parser = action_volume_subparsers.add_parser('expand', add_help=False, help='expand a volume')
        action_volume_expand_parser.add_argument('-v', '--vid', nargs='?', type=int, help='vid of existing volume. If not exists, a new volume is created')
        action_volume_expand_parser.add_argument('-l', '--layout', nargs='?', type=int, choices=[0, 1, 2], help='the layout to set')

        action_volume_expand_parser.add_argument('-t', '--total', nargs='?', type=int, default=1, help='total number of devices by storage')
        action_volume_expand_parser.add_argument('-m', '--mapper', nargs='?', type=int, default=1, help='number of devices used for file to device mapping')
        action_volume_expand_parser.add_argument('-r', '--redundancy', nargs='?', type=int, default=1, help='number of copies of each file to device mapping file')

        action_volume_expand_parser.add_argument('hosts', nargs='+', help='list of nodes to be added.')

        action_volume_remove_parser = action_volume_subparsers.add_parser('remove', add_help=False, help='remove a volume')
        action_volume_remove_parser.add_argument('-v', '--vid', nargs='+', required=True, type=int, help='vid(s) of existing volume.')

        # Export
        action_export_parser = ArgumentParser(add_help=False, prog='rozo export')
        action_export_subparsers = action_export_parser.add_subparsers(help='export available actions', dest='action')

        action_export_create_parser = action_export_subparsers.add_parser('create', add_help=False, help='create an export')
        action_export_create_parser.add_argument('-n', '--name', default=None, help='Name of this export.')
        action_export_create_parser.add_argument('-p', '--passwd', default=None, help='password to set.')
        action_export_create_parser.add_argument('-s', '--squota', default="", help='soft quota to set.')
        action_export_create_parser.add_argument('-a', '--hquota', default="", help='hard quota to set.')
        action_export_create_parser.add_argument('vid', nargs=1, type=int, help='vid of an existing volume.')

        action_export_update_parser = action_export_subparsers.add_parser('update', add_help=False, help='update an export')
        action_export_update_parser.add_argument('-c', '--current', default=None, help='current password.')
        action_export_update_parser.add_argument('-p', '--passwd', default=None, help='password to set.')
        action_export_update_parser.add_argument('-s', '--squota', default=None, help='soft quota to set.')
        action_export_update_parser.add_argument('-a', '--hquota', default=None, help='hard quota to set.')
        action_export_update_parser.add_argument('eid', nargs=1, type=int, help='eid of an existing export.')

        action_export_remove_parser = action_export_subparsers.add_parser('remove', add_help=False, help='remove export(s)')
        action_export_remove_parser.add_argument('-f', '--force', action="store_true", default=False, help='when ever to force removing.')
        action_export_remove_parser.add_argument('eids', nargs='*', type=int, default=None, help='eid(s) of existing export.')

        action_export_get_parser = action_export_subparsers.add_parser('get', add_help=False, help='get export(s) configuration')
        action_export_get_parser.add_argument('-e', '--eids', nargs='+', metavar='EID', type=int, default=None, help='list of eid(s) to display. If not set all exports are displayed.')

        # Mount
        action_mount_parser = ArgumentParser(add_help=False, prog='rozo mount')
        action_mount_subparsers = action_mount_parser.add_subparsers(help='mount available actions', dest='action')

        #action_mount_create_parser = action_mount_subparsers.add_parser('create', add_help=False, help='create an entry for an exported Rozo filesystem in fstab file and mount it', parents=[topic_mount_parser])
        action_mount_create_parser = action_mount_subparsers.add_parser('create', add_help=False, help='create an entry for an exported Rozo filesystem in fstab file and mount it')

        group_mount_create = action_mount_create_parser.add_mutually_exclusive_group()
        group_mount_create.add_argument('-i', '--eids', nargs='+', metavar='EID', type=int, default=None, help='list of export(s) identified by eid to mount.')
        group_mount_create.add_argument('-e', '--exports', nargs='+', metavar='EXPORT_NAME', default=None, help='list of export(s) identified by export_name to mount.')
        action_mount_create_parser.add_argument('-n', '--nodes', nargs='+', help='list of node hostname(s) to mount on.')
        action_mount_create_parser.add_argument('-o', '--options', nargs='+', default=None, help='list of mount option(s) to use.')
        action_mount_create_parser.add_argument('-m', '--mountpoints', nargs='+', default=None, help='list of mountpoints to use on node(s)')

        #action_mount_remove_parser = action_mount_subparsers.add_parser('remove', add_help=False, help='unmount an exported Rozo filesystem and remove it from the fstab file', parents=[topic_mount_parser])
        action_mount_remove_parser = action_mount_subparsers.add_parser('remove', add_help=False, help='unmount an exported Rozo filesystem and remove it from the fstab file')

        group_mount_remove = action_mount_remove_parser.add_mutually_exclusive_group()
        group_mount_remove.add_argument('-i', '--eids', nargs='+', metavar='EID', type=int, default=None, help='list of export(s) identified by eid to unmount.')
        group_mount_remove.add_argument('-e', '--exports', nargs='+', metavar='EXPORT_NAME', default=None, help='list of export(s) identified by export_name to unmount.')
        group_mount_remove.add_argument('-m', '--mountpoints', nargs='+', metavar='MOUNTPOINT', default=None, help='list of mountpoint(s) to unmount.')
        action_mount_remove_parser.add_argument('-n', '--nodes', nargs='+', metavar='NODE', help='list of node hostname(s) to unmount from.')

        # exportd
        subtopic_exportd_parser = ArgumentParser(add_help=False, prog='rozo exportd')

        subtopic_exportd_subparsers = subtopic_exportd_parser.add_subparsers(help='rozo exportd available subtopics', dest='subtopic')

        subtopic_exportd_layout_parser = subtopic_exportd_subparsers.add_parser('layout', help='layout commands', add_help=False)
        subtopic_exportd_option_parser = subtopic_exportd_subparsers.add_parser('option', help='option commands', add_help=False)

        # exportd layout
        action_exportd_layout_parser = ArgumentParser(add_help=False, prog='rozo exportd layout')
        action_exportd_layout_subparser = action_exportd_layout_parser.add_subparsers(help='rozo exportd layout available actions', dest='action')

        ## get
        #action_exportd_layout_get_parser = action_exportd_layout_subparser.add_parser('get', add_help=False, help='get exportd default layout.', parents=[topic_exportd_parser])
        action_exportd_layout_get_parser = action_exportd_layout_subparser.add_parser('get', add_help=False, help='get exportd default layout.')

        ## set
        #action_exportd_layout_set_parser = action_exportd_layout_subparser.add_parser('set', add_help=False, help='set exportd default layout.', parents=[topic_exportd_parser])
        action_exportd_layout_set_parser = action_exportd_layout_subparser.add_parser('set', add_help=False, help='set exportd default layout.')
        action_exportd_layout_set_parser.add_argument('layout', nargs=1, type=int, choices=[0, 1, 2], help='the layout to set.')

        # exportd option
        action_exportd_option_parser = ArgumentParser(add_help=False, prog='rozo exportd option')
        action_exportd_option_subparser = action_exportd_option_parser.add_subparsers(help='rozo exportd option available actions', dest='action')

        ## list
        #action_exportd_option_list_parser = action_exportd_option_subparser.add_parser('list', add_help=False, help='list all exportd option.', parents=[topic_exportd_parser])
        action_exportd_option_list_parser = action_exportd_option_subparser.add_parser('list', add_help=False, help='list all exportd option.')

        ## set
        #action_exportd_option_set_parser = action_exportd_option_subparser.add_parser('set', add_help=False, help='set a specific exportd value option.', parents=[topic_exportd_parser])
        action_exportd_option_set_parser = action_exportd_option_subparser.add_parser('set', add_help=False, help='set a specific exportd value option.')
        action_exportd_option_set_parser.add_argument('option', metavar='OPTION', help='option to set.')
        action_exportd_option_set_parser.add_argument('value', metavar='VALUE', help='value to set.')

        ## get
        #action_exportd_option_get_parser = action_exportd_option_subparser.add_parser('get', add_help=False, help='get a specific exportd value option.', parents=[topic_exportd_parser])
        action_exportd_option_get_parser = action_exportd_option_subparser.add_parser('get', add_help=False, help='get a specific exportd value option.')
        action_exportd_option_get_parser.add_argument('option', metavar='OPTION', help='option to get.')

        # storaged
        subtopic_storaged_parser = ArgumentParser(add_help=False, prog='rozo storaged')

        subtopic_storaged_subparsers = subtopic_storaged_parser.add_subparsers(help='rozo storaged available subtopics', dest='subtopic')

        subtopic_storaged_listen_parser = subtopic_storaged_subparsers.add_parser('listen', help='listen commands', add_help=False)
        subtopic_storaged_option_parser = subtopic_storaged_subparsers.add_parser('option', help='option commands', add_help=False)
        subtopic_storaged_rebuild_parser = subtopic_storaged_subparsers.add_parser('rebuild', help='rebuild commands', add_help=False)

        # storaged listen
        action_storaged_listen_parser = ArgumentParser(add_help=False, prog='rozo storaged listen')
        action_storaged_listen_subparser = action_storaged_listen_parser.add_subparsers(help='rozo storaged listen available actions', dest='action')

        #action_storaged_listen_get_parser = action_storaged_listen_subparser.add_parser('get', add_help=False, help='display listen parameters', parents=[topic_storaged_parser])
        action_storaged_listen_get_parser = action_storaged_listen_subparser.add_parser('get', add_help=False, help='display listen parameters')
        action_storaged_listen_get_parser.add_argument('-n', '--nodes', nargs='+', metavar='NODE', help='list of storaged nodes hostnames to query. If not set all storaged nodes are queried.')

        #action_storaged_listen_add_parser = action_storaged_listen_subparser.add_parser('add', add_help=False, help='add a listener', parents=[topic_storaged_parser])
        action_storaged_listen_add_parser = action_storaged_listen_subparser.add_parser('add', add_help=False, help='add a listener')
        action_storaged_listen_add_parser.add_argument('nodes', nargs='+', metavar='NODE', help='list of storaged nodes hostnames to set.')
        action_storaged_listen_add_parser.add_argument('-i', '--interface', default='*', help='interface to add.')
        action_storaged_listen_add_parser.add_argument('-p', '--port', type=int, default=41001, help='port to add.')

        #action_storaged_listen_remove_parser = action_storaged_listen_subparser.add_parser('remove', add_help=False, help='remove a listener.', parents=[topic_storaged_parser])
        action_storaged_listen_remove_parser = action_storaged_listen_subparser.add_parser('remove', add_help=False, help='remove a listener.')
        action_storaged_listen_remove_parser.add_argument('nodes', nargs='+', metavar='NODE', help='list of storaged nodes hostnames to set.')
        action_storaged_listen_remove_parser.add_argument('-i', '--interface', default='*', help='interface to remove.')
        action_storaged_listen_remove_parser.add_argument('-p', '--port', type=int, default=41001, help='port to remove.')

        # storaged option
        action_storaged_option_parser = ArgumentParser(add_help=False, prog='rozo storaged option')
        action_storaged_option_subparser = action_storaged_option_parser.add_subparsers(help='rozo storaged option available actions', dest='action')

        ## list
        #action_storaged_option_list_parser = action_storaged_option_subparser.add_parser('list', add_help=False, help='list all storaged option.', parents=[topic_storaged_parser])
        action_storaged_option_list_parser = action_storaged_option_subparser.add_parser('list', add_help=False, help='list all storaged option.')
        action_storaged_option_list_parser.add_argument('-n', '--nodes', nargs='+', metavar='NODE', help='list of storaged nodes hostnames to query. If not set all storaged nodes are queried.')

        ## set
        #action_storaged_option_set_parser = action_storaged_option_subparser.add_parser('set', add_help=False, help='set a specific storaged value option.', parents=[topic_storaged_parser])
        action_storaged_option_set_parser = action_storaged_option_subparser.add_parser('set', add_help=False, help='set a specific storaged value option.')
        action_storaged_option_set_parser.add_argument('option', metavar='OPTION', help='option to set.')
        action_storaged_option_set_parser.add_argument('value', metavar='VALUE', help='value to set.')
        action_storaged_option_set_parser.add_argument('-n', '--nodes', metavar='NODE', nargs='+', help='list of storaged servers.')

        ## get
        #action_storaged_option_get_parser = action_storaged_option_subparser.add_parser('get', add_help=False, help='get a specific storaged value option.', parents=[topic_storaged_parser])
        action_storaged_option_get_parser = action_storaged_option_subparser.add_parser('get', add_help=False, help='get a specific storaged value option.')
        action_storaged_option_get_parser.add_argument('option', metavar='OPTION', help='option to get.')
        action_storaged_option_get_parser.add_argument('-n', '--nodes', nargs='+', metavar='NODE', help='list of storaged nodes hostnames to query. If not set all storaged nodes are queried.')

        # storaged rebuild
        action_storaged_rebuild_parser = ArgumentParser(add_help=False, prog='rozo storaged rebuild')
        action_storaged_rebuild_subparser = action_storaged_rebuild_parser.add_subparsers(help='rozo storaged rebuild available actions', dest='action')

        ## start
        #action_storaged_rebuild_start_parser = action_storaged_rebuild_subparser.add_parser('start', add_help=False, help='start to rebuild one storaged node', parents=[topic_storaged_parser])
        action_storaged_rebuild_start_parser = action_storaged_rebuild_subparser.add_parser('start', add_help=False, help='start to rebuild one storaged node')
        action_storaged_rebuild_start_parser.add_argument('-c', '--cid', type=int, help='cluster identifier to rebuild. If not set all storages are rebuilt.')
        action_storaged_rebuild_start_parser.add_argument('-s', '--sid', type=int, help='storage identifier to rebuild. If not set all storages are rebuilt.')
        action_storaged_rebuild_start_parser.add_argument('--device', type=int, help='device number to rebuild. If not set all devices are rebuilt.')
        action_storaged_rebuild_start_parser.add_argument('node', nargs=1, metavar='NODE', help='storaged node to rebuild.')

        #
        # Parsing
        #
        global_namespace, extra = global_parser.parse_known_args()
        # help wanted
        if global_namespace.help:
            if len(extra) == 0 or len(extra) > 3:
                topic_parser.print_help()
                return 0
            if len(extra) == 1:
                try:
                    if extra[0] in ["storaged", "exportd"]:
                        locals()['subtopic_%s_parser' % (extra[0])].print_help()
                    else:
                        locals()['action_%s_parser' % (extra[0])].print_help()
                except KeyError as ke:
                    raise Exception("invalid topic: %s" % extra[0])
            if len(extra) == 2:
                try:
                    locals()['action_%s_%s_parser' % (extra[0], extra[1])].print_help()
                    return 0
                except KeyError as ke:
                    raise Exception("invalid topic: %s or command: %s" % (extra[0], extra[1]))
            if len(extra) == 3:
                try:
                    locals()['action_%s_%s_%s_parser' % (extra[0], extra[1], extra[2])].print_help()
                    return 0
                except KeyError as ke:
                    raise Exception("invalid topic: %s or command: %s" % (extra[0], extra[1]))
            return 0


        # command execution wanted
        # parse topic
        topic_namespace, extra = topic_parser.parse_known_args(extra)

        if topic_namespace.topic in ["storaged", "exportd"]:

            # Parse subtopic
            subtopic_namespace = locals()['subtopic_%s_parser' % topic_namespace.topic].parse_known_args(extra, topic_namespace)

            # Parse action for this subtopic
            action_namespace = locals()['action_%s_%s_parser' % (topic_namespace.topic, subtopic_namespace[0].subtopic)].parse_args(subtopic_namespace[1], subtopic_namespace[0])

        else:
            action_namespace = locals()['action_%s_parser' % topic_namespace.topic].parse_args(extra, topic_namespace)

        m = __import__('rozofs.cli.%s' % topic_namespace.topic, globals(), locals(), ['dispatch'], -1)
        getattr(m, 'dispatch')(action_namespace)
        
        if global_namespace.verbose:
            sys.stdout.write(program_name.upper() + ": SUCCESS\n")

        return 0
    except KeyboardInterrupt:
        ### handle keyboard interrupt ###
        return 0

    except MultipleError, e:
        if global_namespace.debug:
            traceback.print_exc(file=sys.stderr)
            raise(e)

        if global_namespace.verbose:
            sys.stderr.write(program_name.upper() + ": FAILED\n")
            sys.stderr.write('ERRORS:\n' + str(e))
        return 2
        
    except Exception, e:
        if global_namespace.debug:
            traceback.print_exc(file=sys.stderr)
            raise(e)

        sys.stderr.write(program_name.upper() + ": FAILED\n" + str(e) + "\n")

        return 2

if __name__ == "__main__":
    '''RozoFS command line interface.'''
    sys.exit(main())
