Skip to content
Snippets Groups Projects
cli.py 6.18 KiB
Newer Older
#!/usr/bin/env python
#coding=utf8

'''
Seblu's avatar
Seblu committed
CloudControl CLI main module
'''

import os, os.path
import sys
Seblu's avatar
Seblu committed
import re
Seblu's avatar
Seblu committed
import subprocess
Seblu's avatar
Seblu committed
import shlex
import StringIO
Seblu's avatar
Seblu committed

import cccli
Seblu's avatar
Seblu committed
from cccli.exception import *
Seblu's avatar
Seblu committed
from cccli.printer import Printer, color
from cccli.command import Command, Alias
Seblu's avatar
Seblu committed

from sjrpc.client import SimpleRpcClient
Seblu's avatar
Seblu committed
from sjrpc.utils import RpcHandler, pure
Seblu's avatar
Seblu committed
from sjrpc.core.exceptions import *
Seblu's avatar
Seblu committed
    def __init__(self, settings):
        self.settings = settings
Seblu's avatar
Seblu committed
        self.rpc = None
Seblu's avatar
Seblu committed
        self.alias = Alias()
Seblu's avatar
Seblu committed
        self.prompt = ""
Seblu's avatar
Seblu committed
    def start(self, line=""):
Seblu's avatar
Seblu committed
        '''Start a CLI'''
Seblu's avatar
Seblu committed
        # line stuff
Seblu's avatar
Seblu committed
        if line:
Seblu's avatar
Seblu committed
            sys.stdin = StringIO.StringIO(line)
        # start printer
        self.printer = Printer()
Seblu's avatar
Seblu committed
        # set interactive mode
        if sys.stderr.isatty() and sys.stdin.isatty():
            self.printer.debug("Interactive mode")
Seblu's avatar
Seblu committed
            self.prompt = "%s%s%s%s>%s%s%s "%(color["["],
                                              color["light"],
                                              color["]"],
                                              self.settings["login"],
                                              color["["],
                                              color["reset"],
                                              color["]"])
            self.printer.set_interactive()
            # load history
            self.printer.history.load(self.settings.get("history", ""))
            self.printer.history.maxsize(self.settings.get("hsize", None))
            self.printer.debug("Loaded history: %s"%len(self.printer.history))
            # enable completion
            self.printer.completion.set_completer(self._completer)
            # set prompt
        # load alias
        self.alias.load(self.settings.get("alias", ""))
        self.printer.debug("Alias: %s"%self.alias)
        # connecting
        self._connect()
        # auth
        self._auth()
Seblu's avatar
Seblu committed
        self._parse()
        # save history
Seblu's avatar
Seblu committed
        self.printer.history.write(self.settings.get("history", ""))
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
    def _connect(self):
        '''Connect to a cloud control server'''
Seblu's avatar
Seblu committed
        self.printer.debug("Connecting...")
Seblu's avatar
Seblu committed
        try:
            self.rpc = SimpleRpcClient.from_addr(self.settings["server"],
Seblu's avatar
Seblu committed
                                             self.settings["port"],
                                             enable_ssl=True,
                                             default_handler=CliHandler(),
                                             on_disconnect="quit",
                                             timeout=self.settings["timeout"]
Seblu's avatar
Seblu committed
                                        )
            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)
Seblu's avatar
Seblu committed
        self.printer.debug("Connected.")
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
    def _auth(self):
Seblu's avatar
Seblu committed
        '''Handle server authentification'''
Seblu's avatar
Seblu committed
        self.printer.debug("Authenticating...")
            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.")
Seblu's avatar
Seblu committed
    def _parse(self):
        '''Parse a line'''
Seblu's avatar
Seblu committed
        while True:
            try:
Seblu's avatar
Seblu committed
                try:
Seblu's avatar
Seblu committed
                    argv = shlex.split(self.printer.getline(self.prompt), comments=True)
Seblu's avatar
Seblu committed
                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
Seblu's avatar
Seblu committed
            except EOFError:
                break
            except SystemExit:
                break
        self.printer.interactive("tcho!")
Seblu's avatar
Seblu committed
    def _exec_command(self, argv):
        '''Execute command'''
Seblu's avatar
Seblu committed
        self.printer.debug("argv: %s"%argv)
Seblu's avatar
Seblu committed
        try:
Seblu's avatar
Seblu committed
            if (argv[0][0] == "!"):
                argv[0] = argv[0][1:]
                if not len(argv[0]):
                    return
                p = subprocess.Popen(argv, close_fds=True, shell=True)
Seblu's avatar
Seblu committed
                p.wait()
                ret = p.returncode
Seblu's avatar
Seblu committed
            elif (argv[0] == "?"):
Seblu's avatar
Seblu committed
                Command(["help"], self).call()
Seblu's avatar
Seblu committed
            else:
Seblu's avatar
Seblu committed
                Command(argv, self).call()
Seblu's avatar
Seblu committed
        except cmdBadArgument as e:
Seblu's avatar
Seblu committed
            if str(e):
Seblu's avatar
Seblu committed
                self.printer.error("Bad argument: %s."%str(e))
Seblu's avatar
Seblu committed
            else:
Seblu's avatar
Seblu committed
                self.printer.error("Bad argument.")
Seblu's avatar
Seblu committed
            usage = Command.usage(argv[0])
            if usage != "":
Seblu's avatar
Seblu committed
                self.printer.out("usage: %s."%usage)
Seblu's avatar
Seblu committed
        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)))
Seblu's avatar
Seblu committed
        except EOFError:
            self.printer.out("")
Seblu's avatar
Seblu committed
        except Exception as e:
            if cccli.debug:
                raise
Seblu's avatar
Seblu committed
            self.printer.error("%s: %s"%(type(e), str(e)))
            self.printer.warn("This is a not expected error, please report it!")
    def _completer(self, texte, state):
        '''Return the list of completion'''
        comp = self.printer.completion
        stripped = comp.get_buf()[:comp.get_begin() + 1].lstrip()
        if state == 0 and texte == "" and stripped != "":
            return None
        cl = [ c for c in  Command.list() if c.startswith(texte) ]
        if state < len(cl):
            return cl[state]
        return None


class CliHandler(RpcHandler):
    '''Handle RPC incoming request'''

    @pure
    def get_tags(self, tags=()):
        if "version" in tags:
            return { "version": cccli.version }
        Printer().fatal("Disconnected from server!")