cli.py 5.31 KiB
#!/usr/bin/env python
#coding=utf8
'''
CloudControl CLI main module
'''
import os, os.path
import sys
import re
import subprocess
import shlex
import StringIO
import cccli
from cccli.command import Command, Alias
from cccli.printer import Printer, color
from cccli.exception import *
from sjrpc.client import SimpleRpcClient
from sjrpc.utils import RpcHandler, ConnectionProxy, pure
from sjrpc.core.exceptions import *
class Cli(object):
def __init__(self, settings):
self.settings = settings
self.prompt = "> "
self.rpc = None
self.alias = Alias()
def start(self, line=""):
'''Start a CLI'''
# line stuff
if line:
sys.stdin = StringIO.StringIO(line)
# set interactive mode
self.interactive = sys.stderr.isatty() and sys.stdin.isatty()
# start printer and load history
self.printer = Printer(self.interactive,
self.settings.get("forceyes", False),
historyfile=self.settings.get("history", ""),
historysize=self.settings.get("hsize", None))
# load alias
self.alias.load(self.settings.get("alias", ""))
# print debug
self.printer.debug("Interactive: %s"%self.interactive)
self.printer.debug("Alias: %s"%self.alias)
self.printer.debug("Loaded history: %s"%len(self.printer.history))
# connecting
self._connect()
# auth
self._auth()
# parsing
self._parse()
# save history
self.printer.history.save(self.settings.get("history", ""))
def _connect(self):
'''Connect to a cloud control server'''
self.printer.debug("Connecting...")
try:
self.rpc = SimpleRpcClient.from_addr(self.settings["server"],
self.settings["port"],
enable_ssl=True,
default_handler=CliHandler(),
on_disconnect="quit",
timeout=self.settings["timeout"]
)
self.rpc.start(daemonize=True)
except Exception as e:
s = "Connection failure!" if not str(e) else "Connection failure: %s"%str(e)
raise cliError(s)
self.printer.debug("Connected.")
def _auth(self):
'''Handle server authentification'''
self.printer.debug("Authenticating...")
try:
self.rpc.call("authentify", self.settings["login"], self.settings["pass"])
except Exception as e:
s = "Authentication failure!" if not str(e) else "Authentication failure: %s"%str(e)
raise cliError(s)
self.printer.debug("Authenticated.")
def _parse(self):
'''Parse a line'''
if self.interactive:
prompt = "\001%s\002%s>\001%s\002 "%(color["light"],self.settings["login"],color["reset"])
else:
prompt = ""
while True:
try:
try:
argv = shlex.split(self.printer.getline(prompt), comments=True)
except ValueError as e:
self.printer.error("Lexer: %s"%str(e))
continue
if len(argv) == 0:
continue
# alias subsitution
if argv[0] in self.alias:
oldargv = argv[1:]
argv = shlex.split(self.alias[argv[0]])
argv.extend(oldargv)
self._exec_command(argv)
except KeyboardInterrupt:
self.printer.out("")
continue
except EOFError:
break
except SystemExit:
break
if self.interactive:
self.printer.out("tcho!")
def _exec_command(self, argv):
'''Execute command'''
try:
if (argv[0] == "!"):
p = subprocess.Popen(argv[1:], close_fds=True, shell=True)
p.wait()
ret = p.returncode
elif (argv[0] == "?"):
Command(["help"], self).call()
else:
Command(argv, self).call()
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])
if usage != "":
self.printer.out("usage: %s."%usage)
except cmdBadName:
self.printer.error("No command: %s."%argv[0])
except cmdWarning as e:
self.printer.warn("%s: %s"%(argv[0], str(e)))
except cmdError as e:
self.printer.error("%s: %s"%(argv[0], str(e)))
except Exception as e:
if cccli.debug:
raise
self.printer.error(str(e))
self.printer.warn("This is a not expected error, please report it!")
class CliHandler(RpcHandler):
'''Handle RPC incoming request'''
@pure
def get_tags(self, tags=()):
if "version" in tags:
return { "version": cccli.version }
@pure
def quit(self, rpc=None):
Printer().fatal("Disconnected from server!")