Skip to content
Snippets Groups Projects
command.py 18.4 KiB
Newer Older
Seblu's avatar
Seblu committed
#!/usr/bin/env python
#coding=utf8

'''
CloudControl CLI command module
'''

Seblu's avatar
Seblu committed
import os, os.path
import sys
Seblu's avatar
Seblu committed
import re
Seblu's avatar
Seblu committed
import pprint
Seblu's avatar
Seblu committed
import ConfigParser
Seblu's avatar
Seblu committed
import code
Seblu's avatar
Seblu committed
from optparse import OptionParser
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
import cccli
Seblu's avatar
Seblu committed
from cccli.exception import *
from cccli.printer import color

from sjrpc.client import SimpleRpcClient
Seblu's avatar
Seblu committed
from sjrpc.utils import ConnectionProxy
Seblu's avatar
Seblu committed
from sjrpc.core.exceptions import *
Seblu's avatar
Seblu committed

class Command(object):

Seblu's avatar
Seblu committed
    def __init__(self, argv, cli):
Seblu's avatar
Seblu committed
        # check argv
Seblu's avatar
Seblu committed
        if len(argv) == 0:
            raise cmdBadName()
Seblu's avatar
Seblu committed
        # check valid command chars
        if not re.match("^[a-zA-Z0-9]+", argv[0]):
Seblu's avatar
Seblu committed
            raise cmdBadName()
Seblu's avatar
Seblu committed
        cmdlist = [ x[4:] for x in dir(self) if x.startswith("cmd_") ]
        matchlist = [ x for x in cmdlist if re.match("%s.+"%argv[0], x) ]
        if argv[0] in cmdlist:
            pass
        elif len(matchlist) == 1:
            argv[0] = matchlist[0]
        else:
Seblu's avatar
Seblu committed
            raise cmdBadName()
Seblu's avatar
Seblu committed
        self._cmd = getattr(self, "cmd_%s"%argv[0])
Seblu's avatar
Seblu committed
        self._argv = argv
Seblu's avatar
Seblu committed
        self.cli = cli
Seblu's avatar
Seblu committed
        self.printer = cli.printer
Seblu's avatar
Seblu committed

    @classmethod
    def usage(cls, cmdname):
        '''Return usage of a command'''
        fname = "cmd_%s"%cmdname
        if not hasattr(cls, fname):
Seblu's avatar
Seblu committed
            raise cmdBadName(cmdname)
Seblu's avatar
Seblu committed
        if hasattr(getattr(cls, fname), "usage"):
            return getattr(getattr(cls, fname), "usage")
        return ""
Seblu's avatar
Seblu committed

    def call(self):
        '''Run command'''
        if re.match("^[a-zA-Z0-9]+$", self._argv[0]):
            name = "cmd_%s"%self._argv[0]
            if hasattr(self, name):
                cmd = getattr(self, name)
                return cmd(self._argv)
Seblu's avatar
Seblu committed
        raise cmdBadName(self._argv[0])
Seblu's avatar
Seblu committed

    def cmd_exit(self, argv):
        '''Quit application with respect'''
        raise SystemExit()
Seblu's avatar
Seblu committed
    cmd_exit.usage = "exit"
    cmd_exit.desc = "Quit application with respect"

    def cmd_quit(self, argv):
        '''Quit application with respect'''
        raise SystemExit()
    cmd_quit.usage = "quit"
    cmd_quit.desc = "Quit application with respect"

    def cmd_version(self, argv):
        '''Print cli version'''
Seblu's avatar
Seblu committed
        self.printer.out(cccli.version)
Seblu's avatar
Seblu committed
    cmd_version.usage = "version"
    cmd_version.desc = "Print cli version"

    def cmd_usage(self, argv):
        '''Print usage of a command'''
        if len(argv) != 2:
Seblu's avatar
Seblu committed
            raise cmdBadArgument()
Seblu's avatar
Seblu committed
        usage = Command.usage(argv[1])
        if usage != "":
Seblu's avatar
Seblu committed
            self.printer.out("usage: %s"%usage)
Seblu's avatar
Seblu committed
        else:
Seblu's avatar
Seblu committed
            self.printer.out("No usage.")
Seblu's avatar
Seblu committed
    cmd_usage.usage = "usage [command]"
    cmd_usage.desc = "Print usage of a command"

    def cmd_help(self, argv):
        '''Print help'''
        if len(argv) == 1:
            # build command list
            cmdlist = list()
            doclist = list()
            for x in dir(self):
                m = re.match("^cmd_([a-zA-Z0-9]+)$", x)
                if m:
                    cmdlist.append(m.group(1))
                    if hasattr(getattr(self, x), "__doc__"):
                        doclist.append(getattr(getattr(self, x), "__doc__"))
            # printing commands list
            width = max(map(len, cmdlist)) + 3
Seblu's avatar
Seblu committed
            self.printer.out("%sCommands:%s"%(color["lwhite"], color["reset"]))
Seblu's avatar
Seblu committed
            for c, d in zip(cmdlist, doclist):
                line = "%s"%c
                line = line.ljust(width,)
                line += "- %s"%d
Seblu's avatar
Seblu committed
                self.printer.out(line)
Seblu's avatar
Seblu committed
        elif len(argv) == 2:
            fname = "cmd_%s"%argv[1]
            if hasattr(self, fname):
                if hasattr(getattr(self, fname), "__doc__"):
Seblu's avatar
Seblu committed
                    self.printer.out("Description: %s"%getattr(getattr(self, fname), "__doc__"))
Seblu's avatar
Seblu committed
                if hasattr(getattr(self, fname), "usage"):
Seblu's avatar
Seblu committed
                    self.printer.out("Usage: %s"%getattr(getattr(self, fname), "usage"))
Seblu's avatar
Seblu committed
                if hasattr(getattr(self, fname), "details"):
Seblu's avatar
Seblu committed
                    Self.Printer.out("Details: %s"%getattr(getattr(self, fname), "details"))
Seblu's avatar
Seblu committed
            else:
Seblu's avatar
Seblu committed
                raise cmdBadArgument(argv[1])
Seblu's avatar
Seblu committed
        else:
Seblu's avatar
Seblu committed
            raise cmdBadArgument()
Seblu's avatar
Seblu committed
    cmd_help.usage = "help [command]"
    cmd_help.desc = "Print help about a command"

    def cmd_alias(self, argv):
        '''Show or create alias'''
        if len(argv) == 1:
            for n, v in self.cli.alias.items():
Seblu's avatar
Seblu committed
                self.printer.out("%s=%s"%(n, v))
Seblu's avatar
Seblu committed
        elif len(argv) == 2:
            if argv[1] not in self.cli.alias:
Seblu's avatar
Seblu committed
                raise cmdBadArgument(argv[1])
Seblu's avatar
Seblu committed
            self.printer.out("%s=%s"%(argv[1], self.cli.alias[argv[1]]))
Seblu's avatar
Seblu committed
        elif len(argv) == 3:
            self.cli.alias[argv[1]] = argv[2]
Seblu's avatar
Seblu committed
            self.cli.alias.save(self.cli.settings.get("alias", ""))
Seblu's avatar
Seblu committed
        else:
Seblu's avatar
Seblu committed
            raise cmdBadArgument()
Seblu's avatar
Seblu committed
    cmd_alias.usage = "alias [name] [value]"
    cmd_alias.desc = "Show or create aliases"

    def cmd_unalias(self, argv):
        '''Remove an alias'''
        if len(argv) != 2:
Seblu's avatar
Seblu committed
            raise cmdBadArgument()
Seblu's avatar
Seblu committed
        if argv[1] not in self.cli.alias:
Seblu's avatar
Seblu committed
            raise cmdBadArgument("%s: No such alias"%argv[1])
Seblu's avatar
Seblu committed
        del self.cli.alias[argv[1]]
Seblu's avatar
Seblu committed
        self.cli.alias.save(self.cli.settings.get("alias", ""))
Seblu's avatar
Seblu committed
    cmd_unalias.usage = "unalias [name]"
    cmd_unalias.desc = "Remove an aliases"
Seblu's avatar
Seblu committed

    def cmd_rcmd(self, argv):
        '''Show remote commands'''
        for cmds in self.cli.rpc.call("list_commands"):
Seblu's avatar
Seblu committed
            self.printer.out("%s"%cmds["name"])
Seblu's avatar
Seblu committed
    cmd_rcmd.usage = "rcmd"
    cmd_rcmd.desc = "Print remote command list"
Seblu's avatar
Seblu committed
    def cmd_history(self, argv):
        '''Show history of commands'''
        if not self.printer.history:
Seblu's avatar
Seblu committed
            raise cmdError("not available")
        for l in self.printer.history:
Seblu's avatar
Seblu committed
            self.printer.out(l)
Seblu's avatar
Seblu committed
    cmd_history.usage = "history"
    cmd_history.desc = "Show commands history"

Seblu's avatar
Seblu committed
    def cmd_list(self, argv):
        '''List objects'''
Seblu's avatar
Seblu committed
        try:
Seblu's avatar
Seblu committed
            oparser = OptionParser(prog="list")
            oparser.add_option("-t", action="store_true", dest="table")
            (options, args) = oparser.parse_args(argv[1:])
        except SystemExit:
            return
        if len(args) == 0:
            args.append("a")
        try:
            objs = self.cli.rpc.call("list", str.join("", args))
Seblu's avatar
Seblu committed
        except RpcError as e:
            raise cmdError("RPCError: %s"%str(e))
        if len(objs) == 0:
            return
Seblu's avatar
Seblu committed
        if options.table:
            self._list_table(objs)
        else:
            self._list(objs)
    cmd_list.usage = "list [-t] [--help] tql"
Seblu's avatar
Seblu committed

    def _list(self, objs):
        for o in objs:
            id = o.pop("id")
            tags = " ".join([ "%s%s:%s%s"%(color["green"], t, color["reset"], v) for (t,v) in o.items() ])
Seblu's avatar
Seblu committed
            self.printer.out("%sid:%s%s%s %s"%(color["green"], color["yellow"], id, color["reset"], tags))
Seblu's avatar
Seblu committed

    def _list_table(self, objs):
Seblu's avatar
Seblu committed
        # get all tag list
        tags = dict()
        for o in objs:
            for t,v in o.items():
                tags[t] = max(len(str(v)), tags.get(t, len(str(t))))
        # extract id info
        idsize = tags.pop("id")
        # print titles
Seblu's avatar
Seblu committed
        self.printer.out(color["green"], nl="")
        self.printer.out("id".ljust(idsize+1), nl=" ")
Seblu's avatar
Seblu committed
        for t,v in tags.items():
            self.printer.out(t.ljust(v), nl=" ")
Seblu's avatar
Seblu committed
        self.printer.out(color["reset"])
Seblu's avatar
Seblu committed
        # print obj
        for obj in objs:
            self.printer.out("%s%s%s"%(color["yellow"], obj.pop("id").ljust(idsize+1), color["reset"]) ,nl=" ")
            for (t, v) in tags.items():
                self.printer.out(str(obj.get(t, "")).ljust(v) ,nl=" ")
            self.printer.out()
Seblu's avatar
Seblu committed

    def _vm_action(self, argv, filters=None):
        '''All command about vm are the same'''
Seblu's avatar
Seblu committed
        try:
            oparser = OptionParser(prog=argv[0])
            oparser.add_option("--raw", action="store_true", dest="raw",
                               help="Don't append filter on request")
            oparser.add_option("--direct", action="store_true", dest="direct",
                               help="Directly send tql to server (don't list before)")
            oparser.add_option("--force", action="store_true", dest="force",
                               help="Don't ask confirmation (Dangerous)")
            (options, args) = oparser.parse_args(argv[1:])
        except SystemExit:
            return
        if len(args) == 0:
            raise cmdBadArgument()
        tql = str.join("", args)
        # append securty options by command name
        if filters is not None and not options.raw:
            tql += filters
        if options.direct:
            try:
                objs = self.cli.rpc.call(argv[0], tql)
            except RpcError as e:
                raise cmdError("RPCError: %s"%str(e))
        else:
            # get objects id
            try:
                objs = self.cli.rpc.call("list", tql)
            except RpcError as e:
                raise cmdError("RPCError: %s"%str(e))
            # no result, goodbye
            if len(objs) == 0:
                raise cmdWarning("tql: '%s': No result."%tql)
            self.printer.out("You will %s:"%argv[0])
            for obj in objs:
                self.printer.out("%sid:%s%s%s"%(color["green"],color["yellow"],obj["id"],color["reset"]))
            self.printer.out("%sCount: %s%s"%(color["green"],color["reset"], len(objs)))
            # be sure boby want do that
            if not options.force:
                self.printer.out("%sProceed?%s"%(color["lred"], color["reset"]))
                if self.printer.ask("Answer (yes/NO): ") != "yes":
                    raise cmdWarning("Aborted")
                if len(objs) > 5:
                    self.printer.out("%sYou request is on more than 5 objets!%s"%(color["yellow"], color["reset"]))
                    self.printer.out("%sAre you really sure?%s"%(color["lred"], color["reset"]))
                    if self.printer.ask("Answer (Sir, yes Sir!): ") != "Sir, yes Sir!":
                        raise cmdWarning("Bad Answer. Aborted")
            # execute action for each object
            for obj in objs:
                self.printer.out("%s%s%s %s%s%s"%(color["lblue"],
                                                  argv[0],
                                                  color["reset"],
                                                  color["yellow"],
                                                  obj["id"],
                                                  color["reset"]))
                try:
                    self.cli.rpc.call(argv[0], "id:%s"%obj["id"])
                except RpcError as e:
                    self.printer.error("RPCError: %s"%str(e))
Seblu's avatar
Seblu committed
    def cmd_start(self, argv):
        '''Start objects'''
        self._vm_action(argv, "&role=vm&status=stopped")
    cmd_start.usage = "start [--raw] [--direct] [--force] [--help] tql"

    def cmd_stop(self, argv):
        '''Stop objects'''
        self._vm_action(argv, "&role=vm&status=running")
    cmd_stop.usage = "stop [--raw] [--direct] [--force] [--help] tql"

    def cmd_pause(self, argv):
        '''Pause objects'''
        self._vm_action(argv, "&role=vm&status=running")
    cmd_pause.usage = "pause [--raw] [--direct] [--force] [--help] tql"

    def cmd_resume(self, argv):
        '''Resume objects'''
        self._vm_action(argv, "&role=vm&status=stalled")
    cmd_resume.usage = "resume [--raw] [--direct] [--force] [--help] tql"

    def cmd_destroy(self, argv):
        '''Force objects to stop'''
        self._vm_action(argv, "&role=vm&status!=stopped")
    cmd_destroy.usage = "destroy [--raw] [--direct] [--force] [--help] tql"
Seblu's avatar
Seblu committed
    def cmd_clear(self, argv):
        '''Clear tty'''
        self.printer.out("\033[H\033[2J", nl="")
    cmd_clear.usage = "clear"

Seblu's avatar
Seblu committed
    def cmd_uptime(self, argv):
        '''Show connection uptime'''
        if len(argv) == 1:
            argv.append(self.cli.settings["login"])
        tql = "a~(%s)$con"%"|".join(argv[1:])
        try:
            objs = self.cli.rpc.call("list",tql)
Seblu's avatar
Seblu committed
        except RpcError as e:
            raise cmdError("RPCError: %s"%str(e))
        for o in objs:
            self.printer.out("%s: %ss"%(o["a"],o["con"]))
    cmd_uptime.usage = "uptime [login]"

Seblu's avatar
Seblu committed
    def cmd_tags(self, argv):
        '''List static tags on an account (current by default)'''
        if len(argv) == 1:
            argv.append(self.cli.settings["login"])
        for a in argv[1:]:
Seblu's avatar
Seblu committed
            try:
Seblu's avatar
Seblu committed
                tl = self.cli.rpc.call("tags", a)
                tags = " ".join([ "%s%s:%s%s"%(color["green"], t, color["reset"], v) for (t,v) in tl.items() ])
                self.printer.out("%sa:%s%s%s %s"%(color["green"], color["yellow"], a, color["reset"], tags))
Seblu's avatar
Seblu committed
            except RpcError as e:
                raise cmdError("RPCError: %s"%str(e))
    cmd_tags.usage = "tags [account]"

Seblu's avatar
Seblu committed
    def cmd_addtag(self, argv):
Seblu's avatar
Seblu committed
        '''Add/Modify a static tag on an account'''
Seblu's avatar
Seblu committed
        if len(argv) != 4:
            raise cmdBadArgument()
        try:
            self.cli.rpc.call("addtag", argv[1], argv[2], argv[3])
Seblu's avatar
Seblu committed
        except RpcError as e:
            raise cmdError("RPCError: %s"%str(e))
Seblu's avatar
Seblu committed
    cmd_addtag.usage = "addtag <account> <tag> <value>"
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
    def cmd_deltag(self, argv):
Seblu's avatar
Seblu committed
        '''Delete a static tag from an account'''
Seblu's avatar
Seblu committed
        if len(argv) != 3:
            raise cmdBadArgument()
        try:
            self.cli.rpc.call("deltag", argv[1], argv[2])
Seblu's avatar
Seblu committed
        except RpcError as e:
            raise cmdError("RPCError: %s"%str(e))
    cmd_deltag.usage = "deltag <account> <tag>"

Seblu's avatar
Seblu committed
    def cmd_rights(self, argv):
        '''List account rights (current by default)'''
        # Parse argline
        try:
            oparser = OptionParser(prog=argv[0])
            oparser.add_option("--raw", action="store_true", dest="raw",
                               help="Don't append filter on request")
            (options, args) = oparser.parse_args(argv[1:])
        except SystemExit:
            return
        # append current login if nothing asked
        if len(args) == 0:
            tql = "a=%s"%self.cli.settings["login"]
        else:
            tql = "".join(args)
        # update tql if mode
        if not options.raw:
            tql += "&a"
        # ask server
        try:
            al = self.cli.rpc.call("rights", tql)
        except RpcError as e:
            raise cmdError("RPCError: %s"%str(e))
        # display answer
        for (a, rl) in al.items():
            self.printer.out("%srights of %s%s"%(color["lblue"], a, color["reset"]))
            for i,r in enumerate(rl):
                tags = " ".join([ "%s%s:%s%s"%(color["green"], t, color["reset"], v) for (t,v) in r.items() ])
                self.printer.out("[%s] %s"%(i,tags))
    cmd_rights.usage = "rights [--raw] [--help] [tql]"

    def cmd_addright(self, argv):
        '''Add/edit a right'''
        if len(argv) != 5:
            raise cmdBadArgument()
        try:
            self.cli.rpc.call("addright", argv[1], argv[2], argv[3], argv[4])
        except RpcError as e:
            raise cmdError("RPCError: %s"%str(e))
    cmd_addright.usage = "addright <account> <right tql> <method> <allow>"

    def cmd_delright(self, argv):
        '''Delete a right'''
        if len(argv) != 3:
            raise cmdBadArgument()
        try:
            self.cli.rpc.call("delright", argv[1], int(argv[2]))
        except RpcError as e:
            raise cmdError("RPCError: %s"%str(e))
    cmd_delright.usage = "delright <account> <index>"

Seblu's avatar
Seblu committed
    def cmd_addaccount(self, argv):
        '''Create an account'''
        if len(argv) != 3:
Seblu's avatar
Seblu committed
            raise cmdBadArgument()
        try:
            self.cli.rpc.call("addaccount", argv[1], argv[2])
Seblu's avatar
Seblu committed
        except RpcError as e:
            raise cmdError("RPCError: %s"%str(e))
Seblu's avatar
Seblu committed
    cmd_addaccount.usage = "addaccount <account> <role>"
Seblu's avatar
Seblu committed

    def cmd_delaccount(self, argv):
        '''Delete an account'''
        if len(argv) != 2:
            raise cmdBadArgument()
        try:
            self.cli.rpc.call("delaccount", argv[1])
Seblu's avatar
Seblu committed
        except RpcError as e:
            raise cmdError("RPCError: %s"%str(e))
Seblu's avatar
Seblu committed
    cmd_delaccount.usage = "delaccount <account>"
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
    def cmd_passwd(self, argv):
        '''Change account password'''
Seblu's avatar
Seblu committed
        if len(argv) == 1:
            argv.append("a=%s"%self.cli.settings["login"])
Seblu's avatar
Seblu committed
        if len(argv) == 2:
            if not self.cli.interactive:
                raise cmdError("You must give a password argument in non interactive mode!")
            a = self.printer.getpass("Password: ")
            b = self.printer.getpass("Again: ")
            if a != b:
                raise cmdError("You don't type twice the same password. Aborted")
            argv.append(a)
        elif len(argv) == 3:
            if self.cli.interactive:
                s = "You cannot set password with clear argument in interactive mode.\n"
                s += "*** Think to clean your history! ***"
                raise cmdError(s)
        else:
            raise cmdBadArgument()
        try:
            self.cli.rpc.call("passwd", argv[1], argv[2])
Seblu's avatar
Seblu committed
        except RpcError as e:
            raise cmdError("RPCError: %s"%str(e))
Seblu's avatar
Seblu committed
    cmd_passwd.usage = "passwd [tql] [password]"
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
    def cmd_expert(self, argv):
        '''Switch in expert mode'''
Seblu's avatar
Seblu committed
        h = list(self.printer.history)
        self.printer.history.read(self.cli.settings.get("expert", ""))
Seblu's avatar
Seblu committed
        try:
            local = dict()
            local["cli"] = self.cli
            local["rpc"] = self.cli.rpc
            local["proxy"] = ConnectionProxy(self.cli.rpc)
            c = code.InteractiveConsole(local)
Seblu's avatar
Seblu committed
            c.interact("Use Ctrl+D to go back in CLI. Type dir() to see variables.")
Seblu's avatar
Seblu committed
        finally:
Seblu's avatar
Seblu committed
            self.printer.history.write(self.cli.settings.get("expert", ""))
            self.printer.history.load(h)
Seblu's avatar
Seblu committed
    cmd_expert.usage = "expert"

Seblu's avatar
Seblu committed
    def cmd_whoami(self, argv):
        '''Show connection login'''
        self.printer.out(self.cli.settings["login"])
    cmd_whoami.usage = "whoami"

Seblu's avatar
Seblu committed

class Alias(dict):
    ''' Alias wrapper'''
    def load(self, filename):
        '''load alias from file'''
        if os.access(filename, os.R_OK):
Seblu's avatar
Seblu committed
            fparser = ConfigParser.RawConfigParser()
Seblu's avatar
Seblu committed
            fparser.read(filename)
            if fparser.has_section("alias"):
                self.clear()
                self.update(fparser.items("alias"))

    def save(self, filename):
        '''save alias on file'''
        if os.access(filename, os.R_OK or os.W_OK):
Seblu's avatar
Seblu committed
            fparser = ConfigParser.RawConfigParser()
Seblu's avatar
Seblu committed
            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"))