Commit 98a60864 authored by Seblu's avatar Seblu
Browse files

Improve commands loading

Commands loading is now done by crawling commands directory under cccli module.
All command which starts by Commmand_ and which is a son of Command class are candidate.
parent a98db606
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ CloudControl CLI main module
import cccli
from cccli.exception import *
from cccli.printer import Printer, color
from cccli.commands import Commands, Aliases
from cccli.command import Commands, Aliases
from cccli.handler import CliHandler
from cccli.tagdisplay import TagDisplay
from sjrpc.core.exceptions import *
+120 −0
Original line number Diff line number Diff line
@@ -4,11 +4,126 @@
'''
CloudControl CLI command module
'''
import re
import ConfigParser
import os
import shlex
import imp

from cccli.exception import *
from sjrpc.core.exceptions import *
from cccli.printer import Printer, color
from optparse import OptionParser
import cccli.commands

class Commands(object):
    '''Command manager'''

    def __init__(self, cli):
        # save cli context
        self.cli = cli
        # build command list
        self.cmds = self.load_commands(cccli.commands.__path__[0], Command)
        # build remote function list
        try:
            self.functions = set([ c["name"]  for c in self.cli.rpc.call("functions") ])
        except RpcError as e:
            raise cliError("RPCError: Unable to retrieve remote commands: %s"%str(e))
        # remove not available remote commands
        for cname in tuple(self.cmds):
            cobj = self.cmds[cname](self.cli, cname)
            if isinstance(cobj, RemoteCommand):
                try:
                    if len(cobj.remote_functions()) == 0:
                        raise NotImplementedError("No remote function")
                    if not cobj.remote_functions().issubset(self.functions):
                        del self.cmds[cname]
                        self.cli.printer.debug("Command %s not available"%cname)
                except NotImplementedError as e:
                    self.cli.printer.debug("Command %s lack of remote_functions"%cname)
                    del self.cmds[cname]

    def __len__(self):
        return len(self.cmds)

    def __contains__(self, item):
        return item in self.cmds.keys()

    def __iter__(self):
        return iter(self.cmds.keys())

    def __repr__(self):
        return repr(self.cmds.keys())

    def __call__(self, argv):
        # check argv
        if len(argv) == 0:
            raise cmdBadName()
        # find right commands to call
        if argv[0] not in self:
            matchlist = [ x for x in self if re.match("%s.+"%re.escape(argv[0]), x) ]
            if len(matchlist) == 1:
                argv[0] = matchlist[0]
            else:
                raise cmdBadName()
        # create class and call it
        cmd = self.cmds[argv[0]](self.cli, argv[0])
        return cmd(argv)

    def usage(self, argv0):
        '''Return usage of a command'''
        u = self.cmds[argv0](self.cli, argv0).usage()
        return u if u is not None else ""

    def help(self, argv0):
        '''Return  of a command'''
        h = self.cmds[argv0](self.cli, argv0).help()
        return h if h is not None else ""

    def load_commands(self, path, cls):
        '''Load sublasss of cls from package name'''
        cmds=dict()
        for name in os.listdir(path):
            if name.endswith(".py") and not name.startswith("__"):
                fpath = os.path.join(path, name)
                module = imp.load_source(os.path.splitext(name)[0], fpath)
                for key, entry in module.__dict__.items():
                    try:
                        if issubclass(entry, cls) and entry.__name__.startswith("Command_"):
                            cmds[entry.__name__[8:]] = entry
                    except TypeError:
                        continue
        return cmds


class Aliases(dict):
    ''' Aliases manager'''
    def load(self, filename):
        '''load alias from file'''
        if filename is not None:
            fparser = ConfigParser.RawConfigParser()
            fparser.read(filename)
            if fparser.has_section("alias"):
                self.clear()
                self.update(fparser.items("alias"))

    def save(self, filename):
        '''save alias on file'''
        if filename is not None:
            fparser = ConfigParser.RawConfigParser()
            fparser.read(filename)
            fparser.remove_section("alias")
            fparser.add_section("alias")
            for n,v in self.items():
                fparser.set("alias", n, v)
            fparser.write(open(filename, "w"))

    def substitute(self, argv):
        if argv[0] in self:
            oldargv = argv[1:]
            argv = shlex.split(self[argv[0]])
            argv.extend(oldargv)
        return argv

class Command(object):
    '''Base of all command class'''
@@ -19,6 +134,11 @@ class Command(object):
        self.name = argv0

    def __call__(self, argv):
        '''Code called when command is invoked'''
        raise NotImplementedError

    def name(self):
        '''Name of the command'''
        raise NotImplementedError

    def usage(self):

cccli/command/__init__.py

deleted100644 → 0
+0 −90
Original line number Diff line number Diff line
#!/usr/bin/env python
#coding=utf8

'''
CloudControl CLI Commands Package
'''

__all__ = [
    "Command",
    "OptionCommand",
    "RemoteCommand",
    "TqlCommand",
    "Command_addaccount",
    "Command_addright",
    "Command_addtag",
    "Command_alias",
    "Command_cancel",
    "Command_clear",
    "Command_clone",
    "Command_close",
    "Command_declose",
    "Command_delaccount",
    "Command_delright",
    "Command_deltag",
    "Command_destroy",
    "Command_execute",
    "Command_expert",
    "Command_help",
    "Command_history",
    "Command_jobs",
    "Command_kill",
    "Command_list",
    "Command_migrate",
    "Command_passwd",
    "Command_pause",
    "Command_quit",
    "Command_resume",
    "Command_rights",
    "Command_server",
    "Command_shutdown",
    "Command_start",
    "Command_stop",
    "Command_tagdisplay",
    "Command_tags",
    "Command_unalias",
    "Command_undefine",
    "Command_usage",
    "Command_version",
    "Command_whoami",
]

# importing
from cccli.command.command import Command, OptionCommand, RemoteCommand, TqlCommand
from cccli.command.account import Command_addaccount
from cccli.command.account import Command_close
from cccli.command.account import Command_declose
from cccli.command.account import Command_delaccount
from cccli.command.account import Command_passwd
from cccli.command.alias import Command_alias
from cccli.command.alias import Command_unalias
from cccli.command.cancel import Command_cancel
from cccli.command.execute import Command_execute
from cccli.command.expert import Command_expert
from cccli.command.jobs import Command_jobs
from cccli.command.kill import Command_kill
from cccli.command.list import Command_list
from cccli.command.migrate import Command_migrate
from cccli.command.right import Command_addright
from cccli.command.right import Command_delright
from cccli.command.right import Command_rights
from cccli.command.server import Command_server
from cccli.command.shell import Command_clear
from cccli.command.shell import Command_help
from cccli.command.shell import Command_history
from cccli.command.shell import Command_quit
from cccli.command.shell import Command_usage
from cccli.command.shell import Command_version
from cccli.command.shell import Command_whoami
from cccli.command.shutdown import Command_shutdown
from cccli.command.tag import Command_addtag
from cccli.command.tag import Command_deltag
from cccli.command.tag import Command_tags
from cccli.command.tagdisplay import Command_tagdisplay
from cccli.command.vm import Command_clone
from cccli.command.vm import Command_destroy
from cccli.command.vm import Command_pause
from cccli.command.vm import Command_resume
from cccli.command.vm import Command_start
from cccli.command.vm import Command_stop
from cccli.command.vm import Command_undefine

cccli/commands.py

deleted100644 → 0
+0 −108
Original line number Diff line number Diff line
#!/usr/bin/env python
#coding=utf8

'''
CloudControl CLI commands module
'''
import re
import ConfigParser
import os
import shlex

from cccli.exception import *
from cccli.command import *

class Commands(object):
    '''Command manager'''

    def __init__(self, cli):
        # save cli context
        self.cli = cli
        # build command list
        self.cmds = dict()
        for x in [ x for x in globals() if x.startswith("Command_") ]:
            self.cmds[x[8:]] = globals()[x]
        # build remote function list
        try:
            self.functions = set([ c["name"]  for c in self.cli.rpc.call("functions") ])
        except RpcError as e:
            raise cliError("RPCError: Unable to retrieve remote commands: %s"%str(e))
        # remove not available commands
        for cname in tuple(self.cmds):
            cobj = self.cmds[cname](self.cli, cname)
            if isinstance(cobj, RemoteCommand):
                try:
                    if len(cobj.remote_functions()) == 0:
                        raise NotImplementedError("No remote function")
                    if not cobj.remote_functions().issubset(self.functions):
                        del self.cmds[cname]
                        self.cli.printer.debug("Command %s not available"%cname)
                except NotImplementedError as e:
                    self.cli.printer.debug("Command %s lack of remote_functions"%cname)
                    del self.cmds[cname]

    def __len__(self):
        return len(self.cmds)

    def __contains__(self, item):
        return item in self.cmds.keys()

    def __iter__(self):
        return iter(self.cmds.keys())

    def __repr__(self):
        return repr(self.cmds.keys())

    def __call__(self, argv):
        # check argv
        if len(argv) == 0:
            raise cmdBadName()
        # find right commands to call
        if argv[0] not in self:
            matchlist = [ x for x in self if re.match("%s.+"%re.escape(argv[0]), x) ]
            if len(matchlist) == 1:
                argv[0] = matchlist[0]
            else:
                raise cmdBadName()
        # create class and call it
        cmd = self.cmds[argv[0]](self.cli, argv[0])
        return cmd(argv)

    def usage(self, argv0):
        '''Return usage of a command'''
        u = self.cmds[argv0](self.cli, argv0).usage()
        return u if u is not None else ""

    def help(self, argv0):
        '''Return  of a command'''
        h = self.cmds[argv0](self.cli, argv0).help()
        return h if h is not None else ""

class Aliases(dict):
    ''' Aliases manager'''
    def load(self, filename):
        '''load alias from file'''
        if filename is not None:
            fparser = ConfigParser.RawConfigParser()
            fparser.read(filename)
            if fparser.has_section("alias"):
                self.clear()
                self.update(fparser.items("alias"))

    def save(self, filename):
        '''save alias on file'''
        if filename is not None:
            fparser = ConfigParser.RawConfigParser()
            fparser.read(filename)
            fparser.remove_section("alias")
            fparser.add_section("alias")
            for n,v in self.items():
                fparser.set("alias", n, v)
            fparser.write(open(filename, "w"))

    def substitute(self, argv):
        if argv[0] in self:
            oldargv = argv[1:]
            argv = shlex.split(self[argv[0]])
            argv.extend(oldargv)
        return argv
+8 −0
Original line number Diff line number Diff line
#!/usr/bin/env python
#coding=utf8

'''
CloudControl CLI Commands Package
'''

__all__ = []
Loading