#!/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 <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 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] <tql>" 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] <tql>" def cmd_stop(self, argv): '''Stop vm''' self._vm_action(argv, "&role=vm&status=running") cmd_stop.usage = "stop [--raw] [--direct] [--force] [--help] <tql>" def cmd_pause(self, argv): '''Pause vm''' self._vm_action(argv, "&role=vm&status=running") cmd_pause.usage = "pause [--raw] [--direct] [--force] [--help] <tql>" def cmd_resume(self, argv): '''Resume vm''' self._vm_action(argv, "&role=vm&status=stalled") cmd_resume.usage = "resume [--raw] [--direct] [--force] [--help] <tql>" 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] <tql>" 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 <tql> <tag> <value>" 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 <tql> <tag>" 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 <tql> <right tql> <method> <target>" 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>" 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 <account name> <role>" 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 <tql>" 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 <tql>" 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 <tql>" 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 <tql>" 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 <tql> <command>" 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 <tql>" 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"))