Skip to content
Snippets Groups Projects
Commit b9cf7271 authored by Seblu's avatar Seblu
Browse files

Live free or die hard

parent 0d82cea3
No related branches found
No related tags found
No related merge requests found
==================
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
hv: hypervisor name
vm: virtual machine name
id: a.hv.vm
h: hostname
role: (hypersivor/host/cli/vm)
hvtype: hypervistor type (xen/kvm)
libvirtver: Libvirt version
status: online/offline
vmstatus: Running/Paused/Stoped
pop: Point of Presence
cpu: cpu count
mem: memory size
usedmem: memory used
freemem: 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: total available storage
freesto: free storage
usedsto: used storage
=========== ===========
New release New release
=========== ===========
...@@ -5,3 +95,5 @@ Update version in debian/control ...@@ -5,3 +95,5 @@ Update version in debian/control
Update version in debian/changelog Update version in debian/changelog
Update version in setup.py Update version in setup.py
Update version in cccli/__init__.py Update version in cccli/__init__.py
...@@ -17,17 +17,15 @@ import warnings ...@@ -17,17 +17,15 @@ import warnings
import getpass import getpass
settings = {} settings = {}
alias = {}
try: try:
# parse rc file # parse rc file
if "HOME" in os.environ and os.access("%s/.cc-cli.conf"%os.environ["HOME"], os.R_OK): if "HOME" in os.environ and os.access("%s/.cc-cli.conf"%os.environ["HOME"], os.R_OK):
fparser = ConfigParser.SafeConfigParser() fparser = ConfigParser.SafeConfigParser()
fparser.read("%s/.cc-cli.conf"%os.environ["HOME"]) fparser.read("%s/.cc-cli.conf"%os.environ["HOME"])
settings["alias"] = "%s/.cc-cli.conf"%os.environ["HOME"]
if fparser.has_section("cli"): if fparser.has_section("cli"):
settings = dict(fparser.items("cli")) settings.update(fparser.items("cli"))
if fparser.has_section("alias"):
alias = dict(fparser.items("alias"))
# parse env # parse env
if "CC_SERVER" in os.environ: if "CC_SERVER" in os.environ:
...@@ -36,6 +34,8 @@ try: ...@@ -36,6 +34,8 @@ try:
settings["port"] = os.environ["CC_PORT"] settings["port"] = os.environ["CC_PORT"]
if "CC_LOGIN" in os.environ: if "CC_LOGIN" in os.environ:
settings["login"] = os.environ["CC_LOGIN"] settings["login"] = os.environ["CC_LOGIN"]
if "CC_PASS" in os.environ:
settings["pass"] = os.environ["CC_PASS"]
if "CC_DEBUG" in os.environ: if "CC_DEBUG" in os.environ:
settings["debug"] = "True" settings["debug"] = "True"
...@@ -78,8 +78,8 @@ try: ...@@ -78,8 +78,8 @@ try:
sys.exit(1) sys.exit(1)
# remove pass to prevent stupid guy # remove pass to prevent stupid guy
if "pass" in settings: #if "pass" in settings:
del settings["pass"] # del settings["pass"]
# debug stuff # debug stuff
if "debug" in settings: if "debug" in settings:
...@@ -88,7 +88,6 @@ try: ...@@ -88,7 +88,6 @@ try:
warnings.filterwarnings("ignore") warnings.filterwarnings("ignore")
printer.debug("Debugging on") printer.debug("Debugging on")
printer.debug("Settings: %s"%settings) printer.debug("Settings: %s"%settings)
printer.debug("Alias: %s"%alias)
# checking needs # checking needs
if settings["server"] == "": if settings["server"] == "":
...@@ -103,15 +102,17 @@ try: ...@@ -103,15 +102,17 @@ try:
settings["login"] = raw_input("Login: ") settings["login"] = raw_input("Login: ")
# asking for password # asking for password
settings["pass"] = getpass.getpass("Password: ") if settings["pass"] == "":
settings["pass"] = getpass.getpass("Password: ")
# start cli # start cli
cli = cli.Cli(settings, alias, args) cli = cli.Cli(settings, args)
cli.start() cli.start()
except KeyboardInterrupt: except KeyboardInterrupt:
exit(1) exit(1)
except Exception, e: except Exception, e:
if cccli.debug: if cccli.debug:
printer.fatal(str(e), exitcode=-1) printer.fatal(str(e), exitcode=-1)
printer.warn("This is a not expected error, please report it!")
raise raise
printer.fatal(str(e)) printer.fatal(str(e))
...@@ -7,8 +7,3 @@ CloudControl CLI ...@@ -7,8 +7,3 @@ CloudControl CLI
version = "1~dev" version = "1~dev"
debug = False debug = False
import printer
import cli
import clierror
import command
...@@ -10,24 +10,30 @@ import sys ...@@ -10,24 +10,30 @@ import sys
import socket import socket
import ssl import ssl
import threading import threading
import subprocess
import ConfigParser
import cccli import cccli
from cccli import printer, command from cccli import printer, command
from cccli.clierror import * from cccli.clierror import *
from cccli.command import Command
from sjrpc.client import SimpleRpcClient from sjrpc.client import SimpleRpcClient
from sjrpc.utils import RpcHandler, pure, threadless, ConnectionProxy from sjrpc.utils import RpcHandler, pure, threadless, ConnectionProxy
import sjrpc.core.exceptions
import re import re
import readline import readline
class Cli(object): class Cli(object):
def __init__(self, settings, alias, args): def __init__(self, settings, args):
self._settings = settings self._settings = settings
self._alias = alias if "alias" in settings:
self.alias = Alias(settings["alias"])
self.alias.load()
self._interactive = sys.stderr.isatty() and sys.stdin.isatty() self._interactive = sys.stderr.isatty() and sys.stdin.isatty()
self._prompt = "> " self._prompt = "> "
self._commands = args self._commands = args
self._rpc = None self.rpc = None
def start(self): def start(self):
'''Start a CLI''' '''Start a CLI'''
...@@ -48,15 +54,18 @@ class Cli(object): ...@@ -48,15 +54,18 @@ class Cli(object):
printer.debug("Connecting...") printer.debug("Connecting...")
rpcc = SimpleRpcClient.from_addr(self._settings["server"], rpcc = SimpleRpcClient.from_addr(self._settings["server"],
self._settings["port"], self._settings["port"],
enable_ssl=True enable_ssl=True,
on_disconnect=self._on_disconnect
) )
# FIXME: wait sjrpc v5 with on_disconnect usable
rpcc.start(daemonize=True) rpcc.start(daemonize=True)
self._rpc = ConnectionProxy(rpcc) self.rpc = ConnectionProxy(rpcc)
def _on_disconnect(self, rpc):
printer.fatal("Disconnected from server!")
def _auth(self): def _auth(self):
printer.debug("Authenticating...") printer.debug("Authenticating...")
if self._rpc.authentify(self._settings["login"], self._settings["pass"]): if self.rpc.authentify(self._settings["login"], self._settings["pass"]):
printer.debug("Authenticated.") printer.debug("Authenticated.")
else: else:
printer.fatal("Autentification failed!") printer.fatal("Autentification failed!")
...@@ -69,8 +78,6 @@ class Cli(object): ...@@ -69,8 +78,6 @@ class Cli(object):
try: try:
line = raw_input(self._prompt) line = raw_input(self._prompt)
self._parse_line(line) self._parse_line(line)
except BadCommand, e:
printer.error("No such command: %s"%e[0])
except EOFError: except EOFError:
printer.out("") printer.out("")
break break
...@@ -78,12 +85,6 @@ class Cli(object): ...@@ -78,12 +85,6 @@ class Cli(object):
break break
except KeyboardInterrupt: except KeyboardInterrupt:
printer.out("") printer.out("")
except Exception, e:
printer.error(e)
if cccli.debug:
raise
else:
printer.error("This is a not expected error, please report it!")
try: try:
pass pass
#readline.write_history_file(self._settings["histfile"]) #readline.write_history_file(self._settings["histfile"])
...@@ -107,10 +108,62 @@ class Cli(object): ...@@ -107,10 +108,62 @@ class Cli(object):
p.wait() p.wait()
ret = p.returncode ret = p.returncode
elif (cmd[0] == "?"): elif (cmd[0] == "?"):
command.Command("help").call() Command("help").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: else:
argv = self._lex_argv(cmd) printer.error("Bad argument.")
command.Command(argv, self._rpc).call() usage = Command.usage(argv[0])
if usage != "":
printer.out("usage: %s."%usage)
except BadCommand:
printer.error("No command: %s."%argv[0])
except sjrpc.core.exceptions.RpcError, e:
if cccli.debug: raise
printer.error("sjRPC: %s"%str(e))
except Exception, e:
if cccli.debug: raise
printer.error("%s: %s."%(argv[0], str(e)))
def _lex_argv(self, string): def _lex_argv(self, string):
'''Lex command argument'''
return string.split(" ") return string.split(" ")
class Alias(dict):
''' Alias wrapper'''
def __init__(self, filename):
self._filename = filename
def load(self):
'''load alias from file'''
if os.access(self._filename, os.R_OK):
fparser = ConfigParser.SafeConfigParser()
fparser.read(self._filename)
if fparser.has_section("alias"):
self.clear()
self.update(fparser.items("alias"))
def save(self):
'''save alias on file'''
if os.access(self._filename, os.R_OK or os.W_OK):
fparser = ConfigParser.SafeConfigParser()
fparser.read(self._filename)
fparser.remove_section("alias")
fparser.add_section("alias")
for n,v in self.items():
fparser.set("alias", n, v)
fparser.write(open(self._filename, "w"))
...@@ -11,3 +11,6 @@ class cliError(Exception): ...@@ -11,3 +11,6 @@ class cliError(Exception):
class BadCommand(cliError): class BadCommand(cliError):
pass pass
class BadArgument(cliError):
pass
import os, os.path
import sys
import re import re
from sjrpc.client import SimpleRpcClient from sjrpc.client import SimpleRpcClient
import cccli
import pprint
from cccli import printer from cccli import printer
from cccli.clierror import * from cccli.clierror import *
class Command(object): class Command(object):
def __init__(self, argv, rpc): def __init__(self, argv, cli):
if len(argv) < 1: if len(argv) < 1:
raise Exception("Empty command") raise BadCommand()
self._argv = argv self._argv = argv
self._rpc = rpc self.cli = cli
@classmethod
def usage(cls, cmdname):
'''Return usage of a command'''
fname = "cmd_%s"%cmdname
if not hasattr(cls, fname):
raise BadArgument(cmdname)
if hasattr(getattr(cls, fname), "usage"):
return getattr(getattr(cls, fname), "usage")
return ""
def call(self): def call(self):
'''Run command''' '''Run command'''
...@@ -20,35 +34,116 @@ class Command(object): ...@@ -20,35 +34,116 @@ class Command(object):
return cmd(self._argv) return cmd(self._argv)
raise BadCommand(self._argv[0]) raise BadCommand(self._argv[0])
def usage(self, cmdname, printf=None):
'''Return usage of a command'''
fname = "cmd_%s"%cmdname
if hasattr(self, fname):
if hasattr(getattr(self, fname), "usage"):
s = getattr(getattr(self, fname), "usage")
if printf != None:
printf(s)
return s
else:
return "No usage"
else:
raise myError("No such command: %s"%cmdname)
def cmd_exit(self, argv): def cmd_exit(self, argv):
'''Quit application with respect''' '''Quit application with respect'''
raise SystemExit() raise SystemExit()
cmd_exit.usage = "exit"
cmd_exit.desc = "Quit application with respect"
def cmd_quit(self, argv):
'''Quit application with respect'''
raise SystemExit()
cmd_quit.usage = "quit"
cmd_quit.desc = "Quit application with respect"
def cmd_version(self, argv):
'''Print cli version'''
printer.out(cccli.version)
cmd_version.usage = "version"
cmd_version.desc = "Print cli version"
def cmd_usage(self, argv):
'''Print usage of a command'''
if len(argv) != 2:
raise BadArgument()
usage = Command.usage(argv[1])
if usage != "":
printer.out("usage: %s"%usage)
else:
printer.out("No usage.")
cmd_usage.usage = "usage [command]"
cmd_usage.desc = "Print usage of a 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
printer.out("%sCommands:%s"%(printer.color["lwhite"], printer.color["reset"]))
for c, d in zip(cmdlist, doclist):
line = "%s"%c
line = line.ljust(width,)
line += "- %s"%d
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__"))
if hasattr(getattr(self, fname), "usage"):
printer.out("Usage: %s"%getattr(getattr(self, fname), "usage"))
if hasattr(getattr(self, fname), "details"):
Printer.out("Details: %s"%getattr(getattr(self, fname), "details"))
else:
raise BadArgument(argv[1])
else:
raise BadArgument()
cmd_help.usage = "help [command]"
cmd_help.desc = "Print help about a command"
def cmd_alias(self, argv):
'''Show or create alias'''
if len(argv) == 1:
for n, v in self.cli.alias.items():
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]]))
elif len(argv) == 3:
self.cli.alias[argv[1]] = argv[2]
self.cli.alias.save()
else:
raise BadArgument()
cmd_alias.usage = "alias [name] [value]"
cmd_alias.desc = "Show or create aliases"
def cmd_unalias(self, argv):
'''Remove an alias'''
if len(argv) != 2:
raise BadArgument()
if argv[1] not in self.cli.alias:
raise BadArgument("%s: No such alias"%argv[1])
del self.cli.alias[argv[1]]
self.cli.alias.save()
cmd_alias.usage = "unalias [name]"
cmd_alias.desc = "Remove an aliases"
def cmd_rcmd(self, argv):
'''Show remote commands'''
for cmds in self.cli.rpc.list_commands():
printer.out("%s"%cmds["name"])
cmd_rcmd.usage = "rcmd"
cmd_rcmd.desc = "Print remote command list"
def cmd_list(self, argv): def cmd_list(self, argv):
'''List something''' '''List something'''
if len(argv) == 1: if len(argv) == 1:
argv.append("hv") argv.append("")
items = self._rpc.list(str.join("", argv[1:])) items = self.cli.rpc.list(str.join("", argv[1:]))
for item in items: for item in items:
for key, val in item.items(): pprint.pprint(item)
printer.out("%s: %s "%(key, val)) #for key, val in item.items():
printer.out("*"*80) # printer.out("%s: %s "%(key, val))
cmd_list.usage = "list [tags]"
cmd_list.desc = "Print information about tags"
def cmd_export(self, argv):
'''List server exported methods'''
for cmds in self._rpc.list_commands():
printer.out("%s"%cmds["name"])
...@@ -56,11 +56,11 @@ def fatal(message, exitcode=42, fd=sys.stderr, nl=os.linesep): ...@@ -56,11 +56,11 @@ def fatal(message, exitcode=42, fd=sys.stderr, nl=os.linesep):
color["reset"]) color["reset"])
, fd, nl) , fd, nl)
if exitcode >= 0: if exitcode >= 0:
exit(exitcode) os._exit(exitcode)
############################################################################### ###############################################################################
def error(message, fd=sys.stderr, nl=os.linesep): def error(message, fd=sys.stderr, nl=os.linesep):
out("%sError%s: %s.%s"%(color["lred"], out("%sError%s: %s%s"%(color["lred"],
color["red"], color["red"],
message, message,
color["reset"]) color["reset"])
...@@ -68,7 +68,7 @@ def error(message, fd=sys.stderr, nl=os.linesep): ...@@ -68,7 +68,7 @@ def error(message, fd=sys.stderr, nl=os.linesep):
############################################################################### ###############################################################################
def warn(message, fd=sys.stderr, nl=os.linesep): def warn(message, fd=sys.stderr, nl=os.linesep):
out("%sWarning%s: %s.%s"%(color["lyellow"], out("%sWarning%s: %s%s"%(color["lyellow"],
color["yellow"], color["yellow"],
message, message,
color["reset"]) color["reset"])
......
...@@ -9,7 +9,7 @@ Standards-Version: 3.8.0 ...@@ -9,7 +9,7 @@ Standards-Version: 3.8.0
Package: cc-cli Package: cc-cli
Architecture: all Architecture: all
Depends: ${misc:Depends}, ${python:Depends}, python (<< 3), python-sjrpc (>= 4) Depends: ${misc:Depends}, ${python:Depends}, python (<< 3), python-sjrpc (>= 5)
XB-Python-Version: ${python:Versions} XB-Python-Version: ${python:Versions}
Description: CloudControl CLI Description: CloudControl CLI
This package provides the Command Line Interface to CloudControl. This package provides the Command Line Interface to CloudControl.
from setuptools import setup from setuptools import setup
import cccli
import os import os
ldesc = open(os.path.join(os.path.dirname(__file__), 'README')).read() ldesc = open(os.path.join(os.path.dirname(__file__), 'README')).read()
setup( setup(
name='cc-cli', name='cc-cli',
version='1', version=cccli.version,
description='CloudControl CLI', description='CloudControl CLI',
long_description=ldesc, long_description=ldesc,
author='Sebastien Luttringer', author='Sebastien Luttringer',
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment