From d320e3413073176906f04b719d650131ae35059c Mon Sep 17 00:00:00 2001 From: Seblu Date: Fri, 4 Feb 2011 19:01:06 +0100 Subject: [PATCH] Global command refactoring --- cccli/cli.py | 16 +- cccli/command.py | 585 ------------------------------------ cccli/command/__init__.py | 20 ++ cccli/command/account.py | 98 ++++++ cccli/command/alias.py | 41 +++ cccli/command/command.py | 18 ++ cccli/command/connection.py | 65 ++++ cccli/command/expert.py | 32 ++ cccli/command/host.py | 35 +++ cccli/command/list.py | 87 ++++++ cccli/command/right.py | 78 +++++ cccli/command/shell.py | 90 ++++++ cccli/command/tag.py | 77 +++++ cccli/command/vm.py | 121 ++++++++ cccli/commands.py | 86 ++++++ 15 files changed, 857 insertions(+), 592 deletions(-) delete mode 100644 cccli/command.py create mode 100644 cccli/command/__init__.py create mode 100644 cccli/command/account.py create mode 100644 cccli/command/alias.py create mode 100644 cccli/command/command.py create mode 100644 cccli/command/connection.py create mode 100644 cccli/command/expert.py create mode 100644 cccli/command/host.py create mode 100644 cccli/command/list.py create mode 100644 cccli/command/right.py create mode 100644 cccli/command/shell.py create mode 100644 cccli/command/tag.py create mode 100644 cccli/command/vm.py create mode 100644 cccli/commands.py diff --git a/cccli/cli.py b/cccli/cli.py index 2f38a62..f97cf86 100644 --- a/cccli/cli.py +++ b/cccli/cli.py @@ -16,7 +16,7 @@ import StringIO import cccli from cccli.exception import * from cccli.printer import Printer, color -from cccli.command import Command, Alias +from cccli.commands import Commands, Alias from cccli.handler import CliHandler from sjrpc.client import SimpleRpcClient @@ -54,10 +54,12 @@ class Cli(object): self.printer.debug("Loaded history: %s"%len(self.printer.history)) # enable completion self.printer.completion.set_completer(self._command_completer) - # set prompt + # load commands + self.commands = Commands(self) + self.printer.debug("Loaded commands: %d"%len(self.commands)) # load alias self.alias.load(self.settings.get("alias", "")) - self.printer.debug("Alias: %s"%self.alias) + self.printer.debug("Loaded aliases: %d"%len(self.alias)) # connecting self._connect() # auth @@ -139,13 +141,13 @@ class Cli(object): argv[0] = "help" # execute command self.printer.debug("argv: %s"%argv) - Command(argv, self).call() + self.commands(argv) except cmdBadArgument as e: if str(e): self.printer.error("Bad argument: %s."%str(e)) else: self.printer.error("Bad argument.") - usage = Command.usage(argv[0]) + usage = self.commands.usage(argv[0]) if usage != "": self.printer.out("usage: %s."%usage) except cmdBadName: @@ -170,5 +172,5 @@ class Cli(object): if len(texte) > 0 and texte[0] == "!": return () if len(texte) > 0 and texte[0] == "?": - return [ "?%s"%c for c in Command.list() + self.alias.keys() if c.startswith(texte[1:]) ] - return [ c for c in Command.list() + self.alias.keys() if c.startswith(texte) ] + return [ "?%s"%c for c in list(self.commands) + self.alias.keys() if c.startswith(texte[1:]) ] + return [ c for c in list(self.commands) + self.alias.keys() if c.startswith(texte) ] diff --git a/cccli/command.py b/cccli/command.py deleted file mode 100644 index e0f7966..0000000 --- a/cccli/command.py +++ /dev/null @@ -1,585 +0,0 @@ -#!/usr/bin/env python -#coding=utf8 - -''' -CloudControl CLI command module -''' - -import os, os.path -import sys -import re -import pprint -import ConfigParser -import code -from optparse import OptionParser - -import cccli -from cccli.exception import * -from cccli.printer import color - -from sjrpc.client import SimpleRpcClient -from sjrpc.utils import ConnectionProxy -from sjrpc.core.exceptions import * - -class Command(object): - - def __init__(self, argv, cli): - # check argv - if len(argv) == 0: - raise cmdBadName() - # check valid command chars - if not re.match("^[a-zA-Z0-9]+", argv[0]): - raise cmdBadName() - 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: - raise cmdBadName() - self._cmd = getattr(self, "cmd_%s"%argv[0]) - self._argv = argv - self.cli = cli - self.printer = cli.printer - - @classmethod - def list(cls): - '''Return a list of command name''' - return [ x[4:] for x in dir(cls) if x.startswith("cmd_") ] - - @classmethod - def usage(cls, cmdname): - '''Return usage of a command''' - fname = "cmd_%s"%cmdname - if not hasattr(cls, fname): - raise cmdBadName(cmdname) - if hasattr(getattr(cls, fname), "usage"): - return getattr(getattr(cls, fname), "usage") - return "" - - 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) - raise cmdBadName(self._argv[0]) - - def cmd_exit(self, argv): - '''Quit application with respect''' - raise SystemExit() - cmd_exit.usage = "exit" - - def cmd_quit(self, argv): - '''Quit application with respect''' - raise SystemExit() - cmd_quit.usage = "quit" - - def cmd_version(self, argv): - '''Print cli version''' - self.printer.out(cccli.version) - cmd_version.usage = "version" - - def cmd_usage(self, argv): - '''Print usage of a command''' - if len(argv) != 2: - raise cmdBadArgument() - usage = Command.usage(argv[1]) - if usage != "": - self.printer.out("usage: %s"%usage) - else: - self.printer.out("No usage.") - cmd_usage.usage = "usage " - - 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 - self.printer.out("%sCommands:%s"%(color["lwhite"], color["reset"])) - for c, d in zip(cmdlist, doclist): - line = "%s"%c - line = line.ljust(width,) - line += "- %s"%d - self.printer.out(line) - elif len(argv) == 2: - fname = "cmd_%s"%argv[1] - if hasattr(self, fname): - if hasattr(getattr(self, fname), "__doc__"): - self.printer.out("Description: %s"%getattr(getattr(self, fname), "__doc__")) - if hasattr(getattr(self, fname), "usage"): - self.printer.out("Usage: %s"%getattr(getattr(self, fname), "usage")) - if hasattr(getattr(self, fname), "details"): - Self.Printer.out("Details: %s"%getattr(getattr(self, fname), "details")) - else: - raise cmdBadArgument(argv[1]) - else: - raise cmdBadArgument() - cmd_help.usage = "help [command]" - - def cmd_alias(self, argv): - '''Show or create alias''' - if len(argv) == 1: - for n, v in self.cli.alias.items(): - self.printer.out("%s=%s"%(n, v)) - elif len(argv) == 2: - if argv[1] not in self.cli.alias: - raise cmdBadArgument(argv[1]) - self.printer.out("%s=%s"%(argv[1], self.cli.alias[argv[1]])) - elif len(argv) == 3: - self.cli.alias[argv[1]] = argv[2] - self.cli.alias.save(self.cli.settings.get("alias", "")) - else: - raise cmdBadArgument() - cmd_alias.usage = "alias [name] [value]" - - def cmd_unalias(self, argv): - '''Remove an alias''' - if len(argv) != 2: - raise cmdBadArgument() - if argv[1] not in self.cli.alias: - raise cmdBadArgument("%s: No such alias"%argv[1]) - del self.cli.alias[argv[1]] - self.cli.alias.save(self.cli.settings.get("alias", "")) - cmd_unalias.usage = "unalias [name]" - - def cmd_remote(self, argv): - '''Show remote command list''' - try: - for cmds in self.cli.rpc.call("list_commands"): - self.printer.out("%s"%cmds["name"]) - except RpcError as e: - raise cmdError("RPCError: %s"%str(e)) - - cmd_remote.usage = "remote" - - def cmd_history(self, argv): - '''Show commands history''' - if not self.printer.history: - raise cmdError("not available") - for l in self.printer.history: - self.printer.out(l) - cmd_history.usage = "history" - - def cmd_list(self, argv): - '''List objects''' - try: - oparser = OptionParser(prog="list") - oparser.add_option("-c", action="store_true", dest="table", - help="column aligment display") - oparser.add_option("-l", action="store_true", dest="align", - help="line aligment display") - (options, args) = oparser.parse_args(argv[1:]) - except SystemExit: - return - if len(args) == 0: - args.append("") - try: - objs = self.cli.rpc.call("list", str.join("", args)) - except RpcError as e: - raise cmdError("RPCError: %s"%str(e)) - if len(objs) == 0: - return - if options.align: - self._list_align(objs) - elif options.table: - self._list_table(objs) - else: - self._list(objs) - cmd_list.usage = "list [-t] [-a] [--help] " - - 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() ]) - self.printer.out("%sid:%s%s%s %s"%(color["green"], color["yellow"], id, color["reset"], tags)) - - def _list_align(self, objs): - # 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)))) - for o in objs: - id = str(o.pop("id")) - line = "%sid:%s%s%s"%(color["green"], color["yellow"], id.ljust(tags["id"] + 2), color["reset"]) - taglist = o.keys() - taglist.sort() - for tagname in taglist: - line += "%s%s:%s%s"%(color["green"], tagname, color["reset"], - str(o[tagname]).ljust(tags[tagname] + 1)) - self.printer.out(line) - - def _list_table(self, objs): - # 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 - self.printer.out(color["green"], nl="") - self.printer.out("id".ljust(idsize+1), nl=" ") - for t,v in tags.items(): - self.printer.out(t.ljust(v), nl=" ") - self.printer.out(color["reset"]) - # 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() - - def _vm_action(self, argv, filters=None): - '''All command about vm are the same''' - 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)) - - def cmd_start(self, argv): - '''Start vm''' - self._vm_action(argv, "&role=vm&status=stopped") - cmd_start.usage = "start [--raw] [--direct] [--force] [--help] " - - def cmd_stop(self, argv): - '''Stop vm''' - self._vm_action(argv, "&role=vm&status=running") - cmd_stop.usage = "stop [--raw] [--direct] [--force] [--help] " - - def cmd_pause(self, argv): - '''Pause vm''' - self._vm_action(argv, "&role=vm&status=running") - cmd_pause.usage = "pause [--raw] [--direct] [--force] [--help] " - - def cmd_resume(self, argv): - '''Resume vm''' - self._vm_action(argv, "&role=vm&status=stalled") - cmd_resume.usage = "resume [--raw] [--direct] [--force] [--help] " - - def cmd_destroy(self, argv): - '''Force vm to stop''' - self._vm_action(argv, "&role=vm&status!=stopped") - cmd_destroy.usage = "destroy [--raw] [--direct] [--force] [--help] " - - def cmd_clear(self, argv): - '''Clear tty''' - self.printer.out("\033[H\033[2J", nl="") - cmd_clear.usage = "clear" - - def cmd_uptime(self, argv): - '''Show connection uptime''' - if len(argv) == 1: - argv.append("a=%s"%self.cli.settings["login"]) - tql = "".join(argv[1:]) + "$con" - try: - objs = self.cli.rpc.call("list", tql) - except RpcError as e: - raise cmdError("RPCError: %s"%str(e)) - for o in objs: - if "a" in o and "con" in o: - self.printer.out("%s: %ss"%(o["a"], o["con"])) - cmd_uptime.usage = "uptime [tql]" - - def cmd_tags(self, argv): - '''List static tags on an account (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: - objs = self.cli.rpc.call("tags", tql) - except RpcError as e: - raise cmdError("RPCError: %s"%str(e)) - # display answer - 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() ]) - self.printer.out("%sid:%s%s%s %s"%(color["green"], color["yellow"], id, color["reset"], tags)) - cmd_tags.usage = "tags [--raw] [--help] [tql]" - - def cmd_addtag(self, argv): - '''Add/Modify a static tag on an account''' - if len(argv) != 4: - raise cmdBadArgument() - try: - self.cli.rpc.call("addtag", argv[1], argv[2], argv[3]) - except RpcError as e: - raise cmdError("RPCError: %s"%str(e)) - cmd_addtag.usage = "addtag " - - def cmd_deltag(self, argv): - '''Delete a static tag from an account''' - if len(argv) != 3: - raise cmdBadArgument() - try: - self.cli.rpc.call("deltag", argv[1], argv[2]) - except RpcError as e: - raise cmdError("RPCError: %s"%str(e)) - cmd_deltag.usage = "deltag " - - 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 " - - 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 " - - def cmd_addaccount(self, argv): - '''Create an account''' - if len(argv) != 3: - raise cmdBadArgument() - try: - self.cli.rpc.call("addaccount", argv[1], argv[2]) - except RpcError as e: - raise cmdError("RPCError: %s"%str(e)) - cmd_addaccount.usage = "addaccount " - - def cmd_delaccount(self, argv): - '''Delete an account''' - if len(argv) != 2: - raise cmdBadArgument() - try: - self.cli.rpc.call("delaccount", argv[1]) - except RpcError as e: - raise cmdError("RPCError: %s"%str(e)) - cmd_delaccount.usage = "delaccount " - - def cmd_passwd(self, argv): - '''Change account password''' - if len(argv) == 1: - argv.append("a=%s"%self.cli.settings["login"]) - if len(argv) == 2: - 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]) - except RpcError as e: - raise cmdError("RPCError: %s"%str(e)) - cmd_passwd.usage = "passwd [tql] [password]" - - def cmd_expert(self, argv): - '''Switch in expert mode''' - h = list(self.printer.history) - self.printer.history.read(self.cli.settings.get("expert", "")) - try: - local = dict() - local["cli"] = self.cli - local["rpc"] = self.cli.rpc - local["proxy"] = ConnectionProxy(self.cli.rpc) - c = code.InteractiveConsole(local) - c.interact("Use Ctrl+D to go back in CLI. Type dir() to see variables.") - finally: - self.printer.history.write(self.cli.settings.get("expert", "")) - self.printer.history.load(h) - cmd_expert.usage = "expert" - - def cmd_whoami(self, argv): - '''Show connection login''' - self.printer.out(self.cli.settings["login"]) - cmd_whoami.usage = "whoami" - - def cmd_close(self, argv): - '''Close accounts''' - if len(argv) != 2: - raise cmdBadArgument() - try: - self.cli.rpc.call("close", argv[1]) - except RpcError as e: - raise cmdError("RPCError: %s"%str(e)) - cmd_close.usage = "close " - - def cmd_declose(self, argv): - '''Open closed accounts''' - if len(argv) != 2: - raise cmdBadArgument() - try: - self.cli.rpc.call("declose", argv[1]) - except RpcError as e: - raise cmdError("RPCError: %s"%str(e)) - cmd_declose.usage = "declose " - - def cmd_kill(self, argv): - '''Kill a server connection''' - if len(argv) != 2: - raise cmdBadArgument() - try: - self.cli.rpc.call("kill", argv[1]) - except RpcError as e: - raise cmdError("RPCError: %s"%str(e)) - cmd_kill.usage = "kill " - - def cmd_exec(self, argv): - '''exec a remote command''' - if len(argv) != 3: - raise cmdBadArgument() - try: - self.cli.rpc.call("exec", argv[1], argv[2]) - except RpcError as e: - raise cmdError("RPCError: %s"%str(e)) - cmd_exec.usage = "exec " - - def cmd_shutdown(self, argv): - '''shutdown a physical host''' - if len(argv) != 2: - raise cmdBadArgument() - try: - self.cli.rpc.call("shutdown", argv[1]) - except RpcError as e: - raise cmdError("RPCError: %s"%str(e)) - cmd_shutdown.usage = "shutdown " - -class Alias(dict): - ''' Alias wrapper''' - def load(self, filename): - '''load alias from file''' - if os.access(filename, os.R_OK): - 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 os.access(filename, os.R_OK or os.W_OK): - 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")) - diff --git a/cccli/command/__init__.py b/cccli/command/__init__.py new file mode 100644 index 0000000..080918c --- /dev/null +++ b/cccli/command/__init__.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +#coding=utf8 + +''' +CloudControl CLI Commands Package +''' + +# bunch of command +from cccli.command.shell import * +from cccli.command.alias import * +from cccli.command.connection import * +from cccli.command.account import * +from cccli.command.right import * +from cccli.command.tag import * +from cccli.command.host import * +from cccli.command.vm import * + +# by command module +from cccli.command.list import Command_list +from cccli.command.expert import Command_expert diff --git a/cccli/command/account.py b/cccli/command/account.py new file mode 100644 index 0000000..52bd7e9 --- /dev/null +++ b/cccli/command/account.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +#coding=utf8 + +''' +CloudControl accounts related commands +''' + +from cccli.exception import * +from sjrpc.core.exceptions import * +from cccli.printer import Printer, color +from cccli.command.command import Command + +class Command_addaccount(Command): + '''Create an account''' + + def __call__(self, argv): + if len(argv) != 3: + raise cmdBadArgument() + try: + self.cli.rpc.call("addaccount", argv[1], argv[2]) + except RpcError as e: + raise cmdError("RPCError: %s"%str(e)) + + def usage(self): + return "usage: addaccount " + +class Command_delaccount(Command): + '''Delete an account''' + + def __call__(self, argv): + if len(argv) != 2: + raise cmdBadArgument() + try: + self.cli.rpc.call("delaccount", argv[1]) + except RpcError as e: + raise cmdError("RPCError: %s"%str(e)) + + def usage(self): + return "usage: delaccount " + + +class Command_close(Command): + '''Disable accounts''' + + def __call__(self, argv): + if len(argv) != 2: + raise cmdBadArgument() + try: + self.cli.rpc.call("close", argv[1]) + except RpcError as e: + raise cmdError("RPCError: %s"%str(e)) + + def usage(self): + return "usage: close " + + +class Command_declose(Command): + '''Enable accounts''' + + def __call__(self, argv): + + if len(argv) != 2: + raise cmdBadArgument() + try: + self.cli.rpc.call("declose", argv[1]) + except RpcError as e: + raise cmdError("RPCError: %s"%str(e)) + + def usage(self): + return "usage: declose " + + +class Command_passwd(Command): + '''Change account password''' + + def __call__(self, argv): + if len(argv) == 1: + argv.append("a=%s"%self.cli.settings["login"]) + if len(argv) == 2: + 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]) + except RpcError as e: + raise cmdError("RPCError: %s"%str(e)) + + def usage(self): + return "usage: passwd [tql] [password]" diff --git a/cccli/command/alias.py b/cccli/command/alias.py new file mode 100644 index 0000000..7a6ac0c --- /dev/null +++ b/cccli/command/alias.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +#coding=utf8 + +''' +CloudControl alias related command +''' + +from cccli.exception import * +from cccli.command.command import Command + +class Command_alias(Command): + '''Show or create alias''' + def __call__(self, argv): + if len(argv) == 1: + for n, v in self.cli.alias.items(): + self.printer.out("%s=%s"%(n, v)) + elif len(argv) == 2: + if argv[1] not in self.cli.alias: + raise cmdBadArgument(argv[1]) + self.printer.out("%s=%s"%(argv[1], self.cli.alias[argv[1]])) + elif len(argv) == 3: + self.cli.alias[argv[1]] = argv[2] + self.cli.alias.save(self.cli.settings.get("alias", "")) + else: + raise cmdBadArgument() + + def usage(self): + return "usage: alias [name] [value]" + +class Command_unalias(Command): + '''Remove an alias''' + def __call__(self, argv): + if len(argv) != 2: + raise cmdBadArgument() + if argv[1] not in self.cli.alias: + raise cmdBadArgument("%s: No such alias"%argv[1]) + del self.cli.alias[argv[1]] + self.cli.alias.save(self.cli.settings.get("alias", "")) + + def usage(self): + return "usage: unalias [name]" diff --git a/cccli/command/command.py b/cccli/command/command.py new file mode 100644 index 0000000..2f2c7e4 --- /dev/null +++ b/cccli/command/command.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python +#coding=utf8 + +''' +CloudControl CLI command module +''' + +class Command(object): + + def __init__(self, cli): + self.cli = cli + self.printer = self.cli.printer + + def usage(self): + return None + + def help(self): + return self.__doc__ diff --git a/cccli/command/connection.py b/cccli/command/connection.py new file mode 100644 index 0000000..bbc5215 --- /dev/null +++ b/cccli/command/connection.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +#coding=utf8 + +''' +CloudControl Connection related commands +''' + +from cccli.exception import * +from sjrpc.core.exceptions import * +from cccli.printer import Printer, color +from cccli.command.command import Command + +class Command_uptime(Command): + '''Show connection uptime''' + + def __call__(self, argv): + if len(argv) == 1: + argv.append("a=%s"%self.cli.settings["login"]) + tql = "".join(argv[1:]) + "$con" + try: + objs = self.cli.rpc.call("list", tql) + except RpcError as e: + raise cmdError("RPCError: %s"%str(e)) + for o in objs: + if "a" in o and "con" in o: + self.printer.out("%s: %ss"%(o["a"], o["con"])) + + def usage(self): + return "usage: uptime [tql]" + +class Command_remote(Command): + '''Show remote command list''' + + def __call__(self, argv): + try: + for cmds in self.cli.rpc.call("list_commands"): + self.printer.out("%s"%cmds["name"]) + except RpcError as e: + raise cmdError("RPCError: %s"%str(e)) + + def usage(self): + return "usage: remote" + +class Command_whoami(Command): + '''Show connection login''' + + def __call__(self, argv): + self.printer.out(self.cli.settings["login"]) + + def usage(self): + return "usage: whoami" + +class Command_kill(Command): + '''Kill a server connection''' + + def __call__(self, argv): + if len(argv) != 2: + raise cmdBadArgument() + try: + self.cli.rpc.call("kill", argv[1]) + except RpcError as e: + raise cmdError("RPCError: %s"%str(e)) + + def usage(self): + return "usage: kill " diff --git a/cccli/command/expert.py b/cccli/command/expert.py new file mode 100644 index 0000000..5d46e5e --- /dev/null +++ b/cccli/command/expert.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +#coding=utf8 + +''' +CloudControl expert command +''' +from cccli.exception import * +from cccli.printer import Printer, color +from cccli.command.command import Command +from sjrpc.utils import ConnectionProxy +import code + +class Command_expert(Command): + '''Switch in expert mode''' + + def __call__(self, argv): + h = list(self.printer.history) + self.printer.history.read(self.cli.settings.get("expert", "")) + try: + local = dict() + local["cli"] = self.cli + local["rpc"] = self.cli.rpc + local["proxy"] = ConnectionProxy(self.cli.rpc) + c = code.InteractiveConsole(local) + c.interact("Use Ctrl+D to go back in CLI. Type dir() to see variables.") + finally: + self.printer.history.write(self.cli.settings.get("expert", "")) + self.printer.history.load(h) + + + def usage(self): + return "usage: expert" diff --git a/cccli/command/host.py b/cccli/command/host.py new file mode 100644 index 0000000..24babee --- /dev/null +++ b/cccli/command/host.py @@ -0,0 +1,35 @@ + +''' +CloudControl physical host related commands +''' + +from cccli.exception import * +from sjrpc.core.exceptions import * +from cccli.printer import Printer, color +from cccli.command.command import Command + +class Command_exec(Command): + '''Execute a command on the remote host''' + def __call__(self, argv): + if len(argv) != 3: + raise cmdBadArgument() + try: + self.cli.rpc.call("exec", argv[1], argv[2]) + except RpcError as e: + raise cmdError("RPCError: %s"%str(e)) + + def usage(self): + return "usage: exec " + +class Command_shutdown(Command): + '''Shutdown a physical host''' + def __call__(self, argv): + if len(argv) != 2: + raise cmdBadArgument() + try: + self.cli.rpc.call("shutdown", argv[1]) + except RpcError as e: + raise cmdError("RPCError: %s"%str(e)) + + def usage(self): + return "usage: shutdown " diff --git a/cccli/command/list.py b/cccli/command/list.py new file mode 100644 index 0000000..0ec210a --- /dev/null +++ b/cccli/command/list.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +#coding=utf8 + +''' +CloudControl list command +''' + +from cccli.exception import * +from sjrpc.core.exceptions import * +from cccli.printer import Printer, color +from cccli.command.command import Command +from optparse import OptionParser + +class Command_list(Command): + '''List objects''' + + def __call__(self, argv): + try: + oparser = OptionParser(prog="list") + oparser.add_option("-c", action="store_true", dest="table", + help="column aligment display") + oparser.add_option("-l", action="store_true", dest="align", + help="line aligment display") + (options, args) = oparser.parse_args(argv[1:]) + except SystemExit: + return + if len(args) == 0: + args.append("") + try: + objs = self.cli.rpc.call("list", str.join("", args)) + except RpcError as e: + raise cmdError("RPCError: %s"%str(e)) + if len(objs) == 0: + return + if options.align: + self._list_align(objs) + elif options.table: + self._list_table(objs) + else: + self._list(objs) + + def usage(self): + return "usage: list [-t] [-a] [--help] " + + 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() ]) + self.printer.out("%sid:%s%s%s %s"%(color["green"], color["yellow"], id, color["reset"], tags)) + + def _list_align(self, objs): + # 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)))) + for o in objs: + id = str(o.pop("id")) + line = "%sid:%s%s%s"%(color["green"], color["yellow"], id.ljust(tags["id"] + 2), color["reset"]) + taglist = o.keys() + taglist.sort() + for tagname in taglist: + line += "%s%s:%s%s"%(color["green"], tagname, color["reset"], + str(o[tagname]).ljust(tags[tagname] + 1)) + self.printer.out(line) + + def _list_table(self, objs): + # 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 + self.printer.out(color["green"], nl="") + self.printer.out("id".ljust(idsize+1), nl=" ") + for t,v in tags.items(): + self.printer.out(t.ljust(v), nl=" ") + self.printer.out(color["reset"]) + # 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() + diff --git a/cccli/command/right.py b/cccli/command/right.py new file mode 100644 index 0000000..434d2c1 --- /dev/null +++ b/cccli/command/right.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +#coding=utf8 + +''' +CloudControl right releated commands +''' + +from cccli.exception import * +from sjrpc.core.exceptions import * +from cccli.printer import Printer, color +from cccli.command.command import Command + +from optparse import OptionParser + +class Command_rights(Command): + '''List account rights (current by default)''' + + def __call__(self, argv): + # 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)) + + def usage(self): + return "usage: rights [--raw] [--help] [tql]" + + +class Command_addright(Command): + '''Add/edit a right''' + + def __call__(self, argv): + 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)) + + def usage(self): + return "usage: addright " + + +class Command_delright(Command): + '''Delete a right''' + + def __call__(self, argv): + 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)) + + def usage(self): + return "usage: delright " diff --git a/cccli/command/shell.py b/cccli/command/shell.py new file mode 100644 index 0000000..1d85f9b --- /dev/null +++ b/cccli/command/shell.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +#coding=utf8 + +''' +CloudControl shells related commands +''' + +from cccli.exception import * +from cccli.printer import Printer, color +from cccli.command.command import Command + +class Command_quit(Command): + '''Quit application with respect''' + def __call__(self, argv): + raise SystemExit() + + def usage(self): + return "usage: quit" + + +class Command_version(Command): + '''Print cli version''' + def __call__(self, argv): + import cccli + self.printer.out(cccli.version) + + def usage(self): + return "usage: version" + + +class Command_history(Command): + '''Show commands history''' + def __call__(self, argv): + if not self.printer.history: + raise cmdError("not available") + for l in self.printer.history: + self.printer.out(l) + + def usage(self): + return "usage: history" + + +class Command_help(Command): + '''Print help''' + def __call__(self, argv): + if len(argv) == 1: + # printing commands list + width = max(map(len, self.cli.commands)) + 3 + self.printer.out("%sCommands:%s"%(color["lwhite"], color["reset"])) + for c in sorted(self.cli.commands): + line = "%s"%c + line = line.ljust(width,) + line += "- %s"%self.cli.commands.help(c) + self.printer.out(line) + elif len(argv) == 2: + if argv[1] not in self.cli.commands: + raise cmdBadArgument(argv[1]) + self.printer.out(self.cli.commands.help(argv[1])) + self.printer.out(self.cli.commands.usage(argv[1])) + else: + raise cmdBadArgument() + + def usage(self): + return "usage: help [command]" + + +class Command_usage(Command): + '''Print usage of a command''' + + def __call__(self, argv): + if len(argv) != 2: + raise cmdBadArgument() + usage = self.cli.commands.usage(argv[1]) + if usage is None: + self.printer.out("No usage.") + else: + self.printer.out(usage) + + def usage(self): + return "usage: usage " + + +class Command_clear(Command): + '''Clear tty''' + + def __call__(self, argv): + self.printer.out("\033[H\033[2J", nl="") + + def usage(self): + return "usage: clear" diff --git a/cccli/command/tag.py b/cccli/command/tag.py new file mode 100644 index 0000000..7313e30 --- /dev/null +++ b/cccli/command/tag.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +#coding=utf8 + +''' +CloudControl tag releated commands +''' + +from cccli.exception import * +from sjrpc.core.exceptions import * +from cccli.printer import Printer, color +from cccli.command.command import Command + +from optparse import OptionParser + +class Command_tags(Command): + '''List static tags on an account (current by default)''' + + def __call__(self, argv): + # 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: + objs = self.cli.rpc.call("tags", tql) + except RpcError as e: + raise cmdError("RPCError: %s"%str(e)) + # display answer + 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() ]) + self.printer.out("%sid:%s%s%s %s"%(color["green"], color["yellow"], id, color["reset"], tags)) + + def usage(self): + return "usage: tags [--raw] [--help] [tql]" + + +class Command_addtag(Command): + '''Add/Modify a static tag on an account''' + + def __call__(self, argv): + if len(argv) != 4: + raise cmdBadArgument() + try: + self.cli.rpc.call("addtag", argv[1], argv[2], argv[3]) + except RpcError as e: + raise cmdError("RPCError: %s"%str(e)) + + def usage(self): + return "usage: addtag " + + +class Command_deltag(Command): + '''Delete a static tag from an account''' + + def __call__(self, argv): + if len(argv) != 3: + raise cmdBadArgument() + try: + self.cli.rpc.call("deltag", argv[1], argv[2]) + except RpcError as e: + raise cmdError("RPCError: %s"%str(e)) + + def usage(self): + return "usage: deltag " diff --git a/cccli/command/vm.py b/cccli/command/vm.py new file mode 100644 index 0000000..5f4f938 --- /dev/null +++ b/cccli/command/vm.py @@ -0,0 +1,121 @@ + +''' +CloudControl VM related commands +''' + +from cccli.exception import * +from sjrpc.core.exceptions import * +from cccli.printer import Printer, color +from cccli.command.command import Command + +from optparse import OptionParser + +class VmCommand(Command): + '''Command for vm style''' + + def _vm_action(self, argv, filters=None): + 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)) + + +class Command_start(VmCommand): + '''Start a stopped vm''' + def __call__(self, argv): + self._vm_action(argv, "&role=vm&status=stopped") + + def usage(self): + return "usage: start [--raw] [--direct] [--force] [--help] " + + +class Command_stop(VmCommand): + '''Stop a running vm''' + + def __call__(self, argv): + self._vm_action(argv, "&role=vm&status=running") + + def usage(self): + return "usage: stop [--raw] [--direct] [--force] [--help] " + + +class Command_destroy(VmCommand): + '''Force a vm to stop''' + + def __call__(self, argv): + self._vm_action(argv, "&role=vm&status!=stopped") + + def usage(self): + return "usage: destroy [--raw] [--direct] [--force] [--help] " + +class Command_pause(VmCommand): + '''Pause a running vm''' + + def __call__(self, argv): + self._vm_action(argv, "&role=vm&status=running") + + def usage(self): + return "usage: pause [--raw] [--direct] [--force] [--help] " + + +class Command_resume(VmCommand): + '''Resume a paused vm''' + + def __call__(self, argv): + self._vm_action(argv, "&role=vm&status=stalled") + + def usage(self): + return "usage: resume [--raw] [--direct] [--force] [--help] " diff --git a/cccli/commands.py b/cccli/commands.py new file mode 100644 index 0000000..9f356b5 --- /dev/null +++ b/cccli/commands.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +#coding=utf8 + +''' +CloudControl CLI commands module +''' +import re +import ConfigParser +import os + +from cccli.exception import * + +class Commands(object): + + def __init__(self, cli): + # save cli context + self.cli = cli + # build command list + from cccli.command import * + self.cmds = dict() + for x in [ x for x in locals() if x.startswith("Command_") ]: + self.cmds[x[8:]] = locals()[x] + + 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.+"%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) + ret = cmd(argv) + del cmd + return ret + + def usage(self, argv0): + '''Return usage of a command''' + u = self.cmds[argv0](self.cli).usage() + return u if u is not None else "" + + def help(self, argv0): + '''Return of a command''' + h = self.cmds[argv0](self.cli).help() + return h if h is not None else "" + +class Alias(dict): + ''' Alias wrapper''' + def load(self, filename): + '''load alias from file''' + if os.access(filename, os.R_OK): + 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 os.access(filename, os.R_OK or os.W_OK): + 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")) + + + -- GitLab