#!/usr/bin/env python #coding=utf8 ''' CloudControl CLI class ''' import os, os.path import sys import socket import ssl import threading import subprocess import ConfigParser import re from cccli import printer, command, version, debug from cccli.command import Command from cccli.clierror import * from sjrpc.core.exceptions import * from sjrpc.client import SimpleRpcClient from sjrpc.utils import RpcHandler, ConnectionProxy, pure class Cli(object): def __init__(self, settings): self.isinteractive = sys.stderr.isatty() and sys.stdin.isatty() self.settings = settings self.prompt = "> " self.rpc = None self.alias = Alias() self.history = History() def start(self, line=""): '''Start a CLI''' # not interactive is command line if line: self.isinteractive = False # start readline and load history if self.isinteractive: import readline self.history.readline = readline self.history.load(self.settings.get("history", "")) self.history.maxsize(self.settings.get("hsize", None)) # load alias self.alias.load(self.settings.get("alias", "")) printer.debug("Alias: %s"%self.alias) # Connecting self._connect() # authentifications self._auth() # run parsing args if line: self._parse_line(line) else: self._shell() self.history.save(self.settings.get("history", "")) def _connect(self): printer.debug("Connecting...") rpcc = SimpleRpcClient.from_addr(self.settings["server"], self.settings["port"], enable_ssl=True, default_handler=CliHandler(), on_disconnect="quit", timeout=self.settings["timeout"] ) rpcc.start(daemonize=True) self.rpc = ConnectionProxy(rpcc) def _auth(self): printer.debug("Authenticating...") if self.rpc.authentify(self.settings["login"], self.settings["pass"]): printer.debug("Authenticated.") else: printer.fatal("Autentification failed!") def _shell(self): '''Shell parser''' while True: try: line = raw_input(self.prompt) self._parse_line(line) except EOFError: printer.out("") break except SystemExit: break except KeyboardInterrupt: printer.out("") printer.out("Tcho!") def _parse_line(self, line): '''Parse a line (more than one command)''' for cmd in line.split(";"): if (cmd.strip() == "" or cmd[0] == "#"): continue elif (cmd[0] == "!"): p = subprocess.Popen(cmd[1:], close_fds=True, shell=True) p.wait() ret = p.returncode elif (cmd[0] == "?"): Command(["help"], self).call() else: self._parse_command(cmd) def _parse_command(self, cmd): try: # lex command argv = self._lex_argv(cmd) # alias subs if argv[0] in self.alias: argv[0] = self.alias[argv[0]] # execute command Command(argv, self).call() except BadArgument, e: if str(e): printer.error("Bad argument: %s."%str(e)) else: printer.error("Bad argument.") usage = Command.usage(argv[0]) if usage != "": printer.out("usage: %s."%usage) except BadCommand, e: if str(e): printer.error("command: %s."%str(e)) else: printer.error("No command: %s."%argv[0]) except RpcError, e: if debug: raise printer.error("sjRPC: %s"%str(e)) except Exception, e: if debug: raise printer.error("%s: %s."%(argv[0], str(e))) def _lex_argv(self, string): '''Lex command argument''' return string.split(" ") class Alias(dict): ''' Alias wrapper''' def load(self, filename): '''load alias from file''' if os.access(filename, os.R_OK): fparser = ConfigParser.SafeConfigParser() 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.SafeConfigParser() 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")) class History(object): '''History class''' def __init__(self, readline=None): self.readline = readline def __nonzero__(self): return not self.readline is None def __getattribute__(self, name): r = object.__getattribute__(self, "readline") if name == "readline": return r if r is None: return lambda *a,**k: None return object.__getattribute__(self, name) def __iter__(self): for i in range(1, len(self)): yield self.readline.get_history_item(i) def __len__(self): return self.readline.get_current_history_length() def load(self, path): '''Load history from a file''' try: self.readline.read_history_file(path) except IOError: pass def save(self, path): '''Save history into path''' try: self.readline.write_history_file(path) except IOError: pass def maxsize(self, size=None): '''Set or return max history size''' if size is not None: self.readline.set_history_length(size) return self.readline.get_history_length() class CliHandler(RpcHandler): '''Handle RPC incoming request''' @pure def get_tags(self, tags=()): if "version" in tags: return { "version": version } @pure def quit(self, rpc=None): printer.fatal("Disconnected from server!")