Commit 1e4aa1e2 authored by Seblu's avatar Seblu
Browse files

global cleaning / bug fixes

* printer and history merged
* printer now handle raw_input and getpass function
* clierror module to cccli.exception
* lot of reorganisation
* history saving of question is disable
* split tql spec and readme
* update tql spec and readme content
parent fa6116b2
Loading
Loading
Loading
Loading
+2 −103
Original line number Diff line number Diff line
==================
Tag Query Language
==================
== by examples ==
# list accounts
> list a

# list hypervisor
> list hv

# list vm
> list vm

# list vm of hypervisor toto
> list hv=toto&vm

# list vm chiche of hypervisor toto
> list hv=toto&vm=chiche

#list vm with 2 cpu
> list vm&cpu=2

#list vm with 2 cpu and mem > 10g
> list vm&cpu=2&mem>10g

#list hypervistor with 2cpu and vm with 2cpu
> list hv&cpu=2&vm&cpu=2

#list hypervistor at least 2cpu and show tags pop and alloc
> list hv&cpu>=2$pop$alloc

== Basics ==
- TQL build a list of objects from left to right
- Every tag can add or remove objects
- Separators create link between tag
- Operators apply only on one tag

== separators of tags ==
& and between tags
$ show a tag

== operators on tags ==
= strict equality
!= not strict equlity
: globing matching
!:not globing matching
~ regex matching
!~ not regex matching
> superior strict
>= superior
< inferior
<= inferior strict

== number facility ==
10k = 1000
10ki = 1024
1m = 1000 ^ 2
1mi = 1024 ^ 2
1g = 1000 ^ 3
1gi = 1024 ^ 3

== well known tags ==
a: account name (eg: bobby)
role: account role (eg: hypersivor/host/cli/vm)
id: a[.vm] (eg: hkvm-itx1-3.slfw-b)
con: connection uptime or offline (eg: 3600/offline)
hv: hypervisor name (eg: kvm-chausette)
vm: virtual machine name (eg: access)
h: hostname (eg: access)
hvtype: hypervistor type (xen/kvm)
libvirtver: Libvirt version
status: Vm status (eg: running/paused/stopped)
pop: Point of Presence
cpu: cpu count
mem: memory size
memused: memory used
memfree: memory free
arch: (x86/x64)
uname: uname of host
uptime: uptime of hostname
load: load average
hvm: hardware virtualisation enabled
alloc: host is allowed to be selected to a migration
nvm: vm count on an hypervisor
version: account version
sto: storage pool names (eg: vg fg)
stovg_type: vg storage pool type (eg: lvm)
stovg_size: vg storage pool size (eg: 1042)
stovg_used: vg storage pool used space (eg: 1)
stovg_free: vg storage pool free space (eg: 1041)
stovg_path: vg storage pool path (eg: /dev/vg/)
stovg_vol: vg storage pool volume list (eg: sex titi toto)
disk: disk index list (eg: 1 2 3 4)
disk1_path: disk 1 path (eg: /dev/vg/sex)
disk1_size: disk 1 size (eg: 1024)
disk1_pool: storage pool back reference (eg: vg) [vmonly]
disk1_voln: storage pool volume back reference (eg: ) [vmonly]

===========
New release
===========
Update version in cccli/__init__.py
Update version in debian/control
Update version in debian/changelog
 No newline at end of file
Update version in setup.py
Update version in cccli/__init__.py

TQL

0 → 100644
+98 −0
Original line number Diff line number Diff line
==================
Tag Query Language
==================
== by examples ==
# list accounts
> list a

# list hypervisor
> list hv

# list vm
> list vm

# list vm of hypervisor toto
> list hv=toto&vm

# list vm chiche of hypervisor toto
> list hv=toto&vm=chiche

#list vm with 2 cpu
> list vm&cpu=2

#list vm with 2 cpu and mem > 10g
> list vm&cpu=2&mem>10g

#list hypervistor with 2cpu and vm with 2cpu
> list hv&cpu=2&vm&cpu=2

#list hypervistor at least 2cpu and show tags pop and alloc
> list hv&cpu>=2$pop$alloc

== Basics ==
- TQL build a list of objects from left to right
- Every tag can add or remove objects
- Separators create link between tag
- Operators apply only on one tag

== separators of tags ==
& and between tags
$ show a tag
# limit output

== operators on tags ==
= strict equality
!= not strict equlity
: globing matching
!:not globing matching
~ regex matching
!~ not regex matching
> superior strict
>= superior
< inferior
<= inferior strict

== number facility ==
10k = 1000
10ki = 1024
1m = 1000 ^ 2
1mi = 1024 ^ 2
1g = 1000 ^ 3
1gi = 1024 ^ 3

== well known tags ==
a: account name (eg: bobby)
role: account role (eg: hypersivor/host/cli/vm)
id: a[.vm] (eg: hkvm-itx1-3.slfw-b)
con: connection uptime or offline (eg: 3600/offline)
hv: hypervisor name (eg: kvm-chausette)
vm: virtual machine name (eg: access)
h: hostname (eg: access)
hvtype: hypervistor type (xen/kvm)
libvirtver: Libvirt version
status: Vm status (eg: running/paused/stopped)
pop: Point of Presence
cpu: cpu count
mem: memory size
memused: memory used
memfree: memory free
arch: (x86/x64)
uname: uname of host
uptime: uptime of hostname
load: load average
hvm: hardware virtualisation enabled
alloc: host is allowed to be selected to a migration
nvm: vm count on an hypervisor
version: account version
sto: storage pool names (eg: vg fg)
stovg_type: vg storage pool type (eg: lvm)
stovg_size: vg storage pool size (eg: 1042)
stovg_used: vg storage pool used space (eg: 1)
stovg_free: vg storage pool free space (eg: 1041)
stovg_path: vg storage pool path (eg: /dev/vg/)
stovg_vol: vg storage pool volume list (eg: sex titi toto)
disk: disk index list (eg: 1 2 3 4)
disk1_path: disk 1 path (eg: /dev/vg/sex)
disk1_size: disk 1 size (eg: 1024)
disk1_pool: storage pool back reference (eg: vg) [vmonly]
disk1_voln: storage pool volume back reference (eg: ) [vmonly]
+14 −11
Original line number Diff line number Diff line
@@ -2,20 +2,21 @@
#coding=utf8

'''
CloudControl CLI
CloudControl CLI Binary
'''

import os, os.path
import sys
import cccli
from cccli import printer,cli
from cccli.clierror import *
import optparse
import ConfigParser
import pprint
import re
import warnings
import getpass

from cccli import version, debug
from cccli.cli import Cli
from cccli.printer import Printer
from cccli.exception import *

settings = {
    "port": "1984",
@@ -23,6 +24,8 @@ settings = {
    "hsize": "100"
    }

printer = Printer(False)

try:
    # parse rc file
    if "HOME" in os.environ:
@@ -48,7 +51,7 @@ try:

    # Parse line argument
    oparser = optparse.OptionParser(usage="usage: %prog [options] [commands]",
                                    version=cccli.version)
                                    version=version)
    oparser.add_option("-d", "--debug",
                       action="store_true",
                       dest="debug",
@@ -95,7 +98,7 @@ try:

    # debug stuff
    if "debug" in settings:
        cccli.debug = bool(settings["debug"])
        debug = bool(settings["debug"])
    else:
        warnings.filterwarnings("ignore")
    printer.debug("Debugging on")
@@ -113,14 +116,14 @@ try:
            raise BadArgument("Invalid %s number"%i)
    # check login
    if "login" not in settings:
        settings["login"] = raw_input("Login: ")
        settings["login"] = printer.ask("Login: ")

    # check password
    if "pass" not in settings:
        settings["pass"] = getpass.getpass("Password: ")
        settings["pass"] = printer.getpass("Password: ")

    # start cli
    cli = cli.Cli(settings)
    cli = Cli(settings)
    cli.start(" ".join(args))
except BadArgument, e:
    printer.error("Bad Argument: %s"%str(e))
@@ -128,7 +131,7 @@ except BadArgument, e:
except KeyboardInterrupt:
    exit(1)
except Exception, e:
    if cccli.debug:
    if debug:
        printer.fatal(str(e), exitcode=-1)
        raise
    printer.warn("This is a not expected error, please report it!")
+32 −110
Original line number Diff line number Diff line
@@ -2,24 +2,22 @@
#coding=utf8

'''
CloudControl CLI class
CloudControl CLI main module
'''

import os, os.path
import sys
import socket
import ssl
import threading
import subprocess
import ConfigParser
import re
import subprocess

from cccli import version, debug
from cccli.command import Command, Alias
from cccli.printer import Printer
from cccli.exception import *

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
from sjrpc.core.exceptions import *

class Cli(object):
    def __init__(self, settings):
@@ -27,23 +25,21 @@ class Cli(object):
        self.settings = settings
        self.prompt = "> "
        self.rpc = None
        self.printer = None
        self.alias = Alias()
        self.history = History(self)

    def start(self, line=""):
        '''Start a CLI'''
        # not interactive is command line
        # not interactive if command line
        if line:
            self.isinteractive = False
        # start readline and load history
        if self.isinteractive:
            import readline
            self.readline = readline
            self.history.load(self.settings.get("history", ""))
            self.history.maxsize(self.settings.get("hsize", None))
        # start printer and load history
        self.printer = Printer(self.isinteractive)
        self.printer.history.load(self.settings.get("history", ""))
        self.printer.history.maxsize(self.settings.get("hsize", None))
        # load alias
        self.alias.load(self.settings.get("alias", ""))
        printer.debug("Alias: %s"%self.alias)
        self.printer.debug("Alias: %s"%self.alias)
        # Connecting
        self._connect()
        # authentifications
@@ -53,11 +49,11 @@ class Cli(object):
            self._parse_line(line)
        else:
            self._shell()
        self.history.save(self.settings.get("history", ""))
        self.printer.history.save(self.settings.get("history", ""))


    def _connect(self):
        printer.debug("Connecting...")
        self.printer.debug("Connecting...")
        rpcc = SimpleRpcClient.from_addr(self.settings["server"],
                                         self.settings["port"],
                                         enable_ssl=True,
@@ -69,26 +65,26 @@ class Cli(object):
        self.rpc = ConnectionProxy(rpcc)

    def _auth(self):
        printer.debug("Authenticating...")
        self.printer.debug("Authenticating...")
        if self.rpc.authentify(self.settings["login"], self.settings["pass"]):
            printer.debug("Authenticated.")
            self.printer.debug("Authenticated.")
        else:
            printer.fatal("Autentification failed!")
            self.printer.fatal("Autentification failed!")

    def _shell(self):
        '''Shell parser'''
        while True:
            try:
                line = raw_input(self.prompt)
                line = self.printer.getline(self.prompt)
                self._parse_line(line)
            except EOFError:
                printer.out("")
                self.printer.out("")
                break
            except SystemExit:
                break
            except KeyboardInterrupt:
                printer.out("")
        printer.out("Tcho!")
                self.printer.out("")
        self.printer.out("Tcho!")

    def _parse_line(self, line):
        '''Parse a line (more than one command)'''
@@ -115,102 +111,28 @@ class Cli(object):
            Command(argv, self).call()
        except BadArgument, e:
            if str(e):
                printer.error("Bad argument: %s."%str(e))
                self.printer.error("Bad argument: %s."%str(e))
            else:
                printer.error("Bad argument.")
                self.printer.error("Bad argument.")
            usage = Command.usage(argv[0])
            if usage != "":
                printer.out("usage: %s."%usage)
                self.printer.out("usage: %s."%usage)
        except BadCommand, e:
            if str(e):
                printer.error("command: %s."%str(e))
                self.printer.error("command: %s."%str(e))
            else:
                printer.error("No command: %s."%argv[0])
                self.printer.error("No command: %s."%argv[0])
        except RpcError, e:
            if debug: raise
            printer.error("sjRPC: %s"%str(e))
            self.printer.error("sjRPC: %s"%str(e))
        except Exception, e:
            if debug: raise
            printer.error("%s: %s."%(argv[0], str(e)))
            self.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, cli):
        self.cli = cli

    def __nonzero__(self):
        return not self.cli.readline is None

    def __getattribute__(self, name):
        r = object.__getattribute__(self, "cli")
        if name == "cli":
            return r
        if r.readline is None:
            return lambda *a,**k: None
        return object.__getattribute__(self, name)

    def __iter__(self):
        for i in range(1, len(self)):
            yield self.cli.readline.get_history_item(i)

    def __len__(self):
        return self.cli.readline.get_current_history_length()

    def load(self, path):
        '''Load history from a file'''
        try:
            self.cli.readline.read_history_file(path)
        except IOError:
            pass

    def save(self, path):
        '''Save history into path'''
        try:
            self.cli.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.cli.readline.set_history_length(size)
        return self.cli.readline.get_history_length()

    def removelast(self):
        '''Remove last history line'''
        self.cli.readline.remove_history_item(self.cli.readline.get_current_history_length())

    def clear(self):
        '''Clear history'''
        self.cli.readline.clear_history()


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

@@ -221,4 +143,4 @@ class CliHandler(RpcHandler):

    @pure
    def quit(self, rpc=None):
        printer.fatal("Disconnected from server!")
        self.printer.fatal("Disconnected from server!")
+57 −23
Original line number Diff line number Diff line
#!/usr/bin/env python
#coding=utf8

'''
CloudControl CLI command module
'''

import os, os.path
import sys
import re
from sjrpc.client import SimpleRpcClient
import cccli
import pprint
from cccli import printer
from cccli.clierror import *
import ConfigParser

from cccli.exception import *
from cccli.printer import color

from sjrpc.client import SimpleRpcClient
from sjrpc.core.exceptions import *

class Command(object):

@@ -29,6 +39,7 @@ class Command(object):
        self._cmd = getattr(self, "cmd_%s"%argv[0])
        self._argv = argv
        self.cli = cli
        self.printer = cli.printer

    @classmethod
    def usage(cls, cmdname):
@@ -63,7 +74,7 @@ class Command(object):

    def cmd_version(self, argv):
        '''Print cli version'''
        printer.out(cccli.version)
        self.printer.out(cccli.version)
    cmd_version.usage = "version"
    cmd_version.desc = "Print cli version"

@@ -73,9 +84,9 @@ class Command(object):
            raise BadArgument()
        usage = Command.usage(argv[1])
        if usage != "":
            printer.out("usage: %s"%usage)
            self.printer.out("usage: %s"%usage)
        else:
            printer.out("No usage.")
            self.printer.out("No usage.")
    cmd_usage.usage = "usage [command]"
    cmd_usage.desc = "Print usage of a command"

@@ -93,21 +104,21 @@ class Command(object):
                        doclist.append(getattr(getattr(self, x), "__doc__"))
            # printing commands list
            width = max(map(len, cmdlist)) + 3
            printer.out("%sCommands:%s"%(printer.color["lwhite"], printer.color["reset"]))
            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
                printer.out(line)
                self.printer.out(line)
        elif len(argv) == 2:
            fname = "cmd_%s"%argv[1]
            if hasattr(self, fname):
                if hasattr(getattr(self, fname), "__doc__"):
                    printer.out("Description: %s"%getattr(getattr(self, fname), "__doc__"))
                    self.printer.out("Description: %s"%getattr(getattr(self, fname), "__doc__"))
                if hasattr(getattr(self, fname), "usage"):
                    printer.out("Usage: %s"%getattr(getattr(self, fname), "usage"))
                    self.printer.out("Usage: %s"%getattr(getattr(self, fname), "usage"))
                if hasattr(getattr(self, fname), "details"):
                    Printer.out("Details: %s"%getattr(getattr(self, fname), "details"))
                    Self.Printer.out("Details: %s"%getattr(getattr(self, fname), "details"))
            else:
                raise BadArgument(argv[1])
        else:
@@ -119,11 +130,11 @@ class Command(object):
        '''Show or create alias'''
        if len(argv) == 1:
            for n, v in self.cli.alias.items():
                printer.out("%s=%s"%(n, v))
                self.printer.out("%s=%s"%(n, v))
        elif len(argv) == 2:
            if argv[1] not in self.cli.alias:
                raise BadArgument(argv[1])
            printer.out("%s=%s"%(argv[1], self.cli.alias[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", ""))
@@ -146,7 +157,7 @@ class Command(object):
    def cmd_rcmd(self, argv):
        '''Show remote commands'''
        for cmds in self.cli.rpc.list_commands():
            printer.out("%s"%cmds["name"])
            self.printer.out("%s"%cmds["name"])
    cmd_rcmd.usage = "rcmd"
    cmd_rcmd.desc = "Print remote command list"

@@ -155,7 +166,7 @@ class Command(object):
        if not self.cli.history:
            raise Exception("History is disabled")
        for l in self.cli.history:
            printer.out(l)
            self.printer.out(l)
    cmd_history.usage = "history"
    cmd_history.desc = "Show commands history"

@@ -167,7 +178,7 @@ class Command(object):
        for item in items:
            pprint.pprint(item)
            #for key, val in item.items():
            #    printer.out("%s: %s "%(key, val))
            #    self.printer.out("%s: %s "%(key, val))
    cmd_list.usage = "list [tql]"

    def _startstopsdestroypauseresume(self, argv):
@@ -178,16 +189,16 @@ class Command(object):
        # print tql list result
        items = self.cli.rpc.list(tql)
        if len(items) == 0:
            printer.out("No selected object")
            self.printer.out("No selected object")
            return
        printer.out("Your request give the following result:")
        self.printer.out("Your request give the following result:")
        for item in items:
            printer.out("%s"%item["id"])
        printer.out("Count: %s"%len(items))
        if raw_input("Are you sure to %s? (yes/NO) "%argv[0]) != "yes":
            self.printer.out("%s"%item["id"])
        self.printer.out("Count: %s"%len(items))
        if self.printer.ask("Are you sure to %s? (yes/NO) "%argv[0]) != "yes":
            raise Exception("Aborted")
        if len(items) > 5:
            if raw_input("You request is on more than 5 objets. Are you really sure to %s its? (Yes, I am) "%argv[0]) != "Yes, I am":
            if self.printer.ask("You request is on more than 5 objets. Are you really sure to %s its? (Yes, I am) "%argv[0]) != "Yes, I am":
                raise Exception("Aborted")
        self.cli.rpc[argv[0]](tql)

@@ -217,3 +228,26 @@ class Command(object):
        self._startstopsdestroypauseresume(argv)
    cmd_destroy.usage = "destroy [tql]"


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"))
Loading