Commit ad3fab96 authored by Stavros Sachtouris's avatar Stavros Sachtouris

Merge branch 'develop'

parents c32dcf79 aa25346e
......@@ -79,7 +79,7 @@ _best_match = []
def _num_of_matching_terms(basic_list, attack_list):
if not attack_list:
return 1
return len(basic_list)
matching_terms = 0
for i, term in enumerate(basic_list):
......@@ -100,6 +100,8 @@ def _update_best_match(name_terms, prefix=[]):
num_of_matching_terms = _num_of_matching_terms(name_terms, pref_list)
global _best_match
if not prefix:
_best_match = []
if num_of_matching_terms and len(_best_match) <= num_of_matching_terms:
if len(_best_match) < num_of_matching_terms:
......@@ -129,6 +131,8 @@ def command(cmd_tree, prefix='', descedants_depth=1):
name_terms = cls_name.split('_')
if not _update_best_match(name_terms, prefix):
if _debug:
print('Warning: %s failed to update_best_match' % cls_name)
return None
global _best_match
......@@ -137,6 +141,8 @@ def command(cmd_tree, prefix='', descedants_depth=1):
partial = '_'.join(name_terms[:max_len])
if not cmd_tree.has_command(partial): # add partial path
cmd_tree.add_command(partial)
if _debug:
print('Warning: %s failed max_len test' % cls_name)
return None
cls.description, sep, cls.long_description\
......@@ -379,27 +385,10 @@ def one_cmd(parser, unparsed, arguments):
_exec_cmd(executable, unparsed, parser.print_help)
from command_shell import _fix_arguments, Shell
def _start_shell():
shell = Shell()
shell.set_prompt(basename(argv[0]))
from kamaki import __version__ as version
shell.greet(version)
shell.do_EOF = shell.do_exit
return shell
def run_shell(arguments):
_fix_arguments()
shell = _start_shell()
_config = _arguments['config']
from kamaki.cli.command_tree import CommandTree
shell.cmd_tree = CommandTree(
'kamaki', 'A command line tool for poking clouds')
def _load_all_commands(cmd_tree, arguments):
_config = arguments['config']
for spec in [spec for spec in _config.get_groups()\
if arguments['config'].get(spec, 'cli')]:
if _config.get(spec, 'cli')]:
try:
spec_module = _load_spec_module(spec, arguments, '_commands')
spec_commands = getattr(spec_module, '_commands')
......@@ -409,9 +398,15 @@ def run_shell(arguments):
continue
for spec_tree in spec_commands:
if spec_tree.name == spec:
shell.cmd_tree.add_tree(spec_tree)
cmd_tree.add_tree(spec_tree)
break
shell.run()
def run_shell(exe_string, arguments):
from command_shell import _init_shell
shell = _init_shell(exe_string, arguments)
_load_all_commands(shell.cmd_tree, arguments)
shell.run(arguments)
def main():
......@@ -433,7 +428,7 @@ def main():
parser.print_help()
_groups_help(_arguments)
else:
run_shell(_arguments)
run_shell(exe, _arguments)
except CLIError as err:
if _debug:
raise err
......
......@@ -116,6 +116,8 @@ class Argument(object):
class ConfigArgument(Argument):
_config_file = None
@property
def value(self):
super(self.__class__, self).value
......@@ -123,7 +125,13 @@ class ConfigArgument(Argument):
@value.setter
def value(self, config_file):
self._value = Config(config_file) if config_file else Config()
if config_file:
self._value = Config(config_file)
self._config_file = config_file
elif self._config_file:
self._value = Config(self._config_file)
else:
self._value = Config()
def get(self, group, term):
return self.value.get(group, term)
......@@ -233,32 +241,41 @@ class ProgressBarArgument(FlagArgument):
self.suffix = '%(percent)d%%'
super(ProgressBarArgument, self).__init__(help, parsed_name, default)
try:
self.bar = IncrementalBar()
IncrementalBar
except NameError:
print('Waring: no progress bar functionality')
def clone(self):
newarg = ProgressBarArgument(
self.help,
self.parsed_name,
self.default)
newarg._value = self._value
return newarg
def get_generator(self, message, message_len=25):
if self.value:
return None
self.bar = IncrementalBar()
try:
bar = ProgressBar(message.ljust(message_len))
self.bar.message = message.ljust(message_len)
except NameError:
return None
return bar.get_generator()
pass
self.bar.suffix = '%(percent)d%% - %(eta)ds'
def progress_gen(n):
for i in self.bar.iter(range(int(n))):
yield
yield
return progress_gen
try:
class ProgressBar(IncrementalBar):
suffix = '%(percent)d%% - %(eta)ds'
def finish(self):
if self.value:
return
mybar = getattr(self, 'bar', None)
if mybar:
mybar.finish()
def get_generator(self):
def progress_gen(n):
for i in self.iter(range(int(n))):
yield
yield
return progress_gen
except NameError:
pass
_arguments = dict(config=_config_arg,
help=Argument(0, 'Show help message', ('-h', '--help')),
......@@ -278,8 +295,8 @@ def parse_known_args(parser, arguments=None):
parsed, unparsed = parser.parse_known_args()
for name, arg in arguments.items():
arg.value = getattr(parsed, name, arg.default)
unparsed = ['"%s"' % s if ' ' in s else s for s in unparsed]
return parsed, unparsed
# ['"%s"' % s if ' ' in s else s for s in unparsed]
def init_parser(exe, arguments):
......
......@@ -37,16 +37,25 @@ from sys import stdout
from argparse import ArgumentParser
from kamaki.cli import _exec_cmd, _print_error_message
from kamaki.cli.argument import _arguments, update_arguments
from kamaki.cli.utils import print_dict
from kamaki.cli.argument import update_arguments
from kamaki.cli.utils import print_dict, split_input
from kamaki.cli.history import History
from kamaki.cli.errors import CLIError
def _fix_arguments():
_arguments.pop('version', None)
_arguments.pop('options', None)
_arguments.pop('history', None)
def _init_shell(exe_string, arguments):
arguments.pop('version', None)
arguments.pop('options', None)
arguments.pop('history', None)
shell = Shell()
shell.set_prompt(exe_string)
from kamaki import __version__ as version
shell.greet(version)
shell.do_EOF = shell.do_exit
from kamaki.cli.command_tree import CommandTree
shell.cmd_tree = CommandTree(
'kamaki', 'A command line tool for poking clouds')
return shell
class Shell(Cmd):
......@@ -55,8 +64,35 @@ class Shell(Cmd):
_suffix = ']:'
cmd_tree = None
_history = None
_arguments = None
_context_stack = []
_prompt_stack = []
undoc_header = 'interactive shell commands:'
def postcmd(self, post, line):
if self._context_stack:
self._roll_command()
self._restore(self._context_stack.pop())
self.set_prompt(self._prompt_stack.pop()[1:-2])
return Cmd.postcmd(self, post, line)
def precmd(self, line):
if line.startswith('/'):
cur_cmd_path = self.prompt.replace(' ', '_')[1:-2]
if cur_cmd_path != self.cmd_tree.name:
cur_cmd = self.cmd_tree.get_command(cur_cmd_path)
self._context_stack.append(self._backup())
self._prompt_stack.append(self.prompt)
new_context = self
self._roll_command(cur_cmd.path)
new_context.set_prompt(self.cmd_tree.name)
for grp_cmd in self.cmd_tree.get_subcommands():
self._register_command(grp_cmd.path)
return line[1:]
return line
def greet(self, version):
print('kamaki v%s - Interactive Shell\n\t(exit or ^D to exit)\n'\
% version)
......@@ -66,6 +102,8 @@ class Shell(Cmd):
def do_exit(self, line):
print('')
if self.prompt[1:-2] == self.cmd_tree.name:
exit(0)
return True
def do_shell(self, line):
......@@ -89,7 +127,7 @@ class Shell(Cmd):
except KeyError:
pass
def _roll_command(self, cmd_path):
def _roll_command(self, cmd_path=None):
for subname in self.cmd_tree.get_subnames(cmd_path):
self._unregister_method('do_%s' % subname)
self._unregister_method('complete_%s' % subname)
......@@ -103,34 +141,29 @@ class Shell(Cmd):
def _restore(self, oldcontext):
self.__dict__ = oldcontext
def _push_in_command(self, cmd_path):
def _register_command(self, cmd_path):
cmd = self.cmd_tree.get_command(cmd_path)
self.cmd_tree = self.cmd_tree
_history = self._history
arguments = self._arguments
def do_method(self, line):
def do_method(new_context, line):
""" Template for all cmd.Cmd methods of the form do_<cmd name>
Parse cmd + args and decide to execute or change context
<cmd> <term> <term> <args> is always parsed to most specific
even if cmd_term_term is not a terminal path
"""
if _history:
_history.add(' '.join([cmd.path.replace('_', ' '), line]))
subcmd, cmd_args = cmd.parse_out(line.split())
active_terms = [cmd.name] +\
subcmd.path.split('_')[len(cmd.path.split('_')):]
subname = '_'.join(active_terms)
cmd_parser = ArgumentParser(subname, add_help=False)
subcmd, cmd_args = cmd.parse_out(split_input(line))
if self._history:
self._history.add(' '.join([cmd.path.replace('_', ' '), line]))
cmd_parser = ArgumentParser(cmd.name, add_help=False)
cmd_parser.description = subcmd.help
# exec command or change context
if subcmd.is_command: # exec command
cls = subcmd.get_class()
instance = cls(dict(_arguments))
instance = cls(dict(arguments))
cmd_parser.prog = '%s %s' % (cmd_parser.prog.replace('_', ' '),
cls.syntax)
update_arguments(cmd_parser, instance.arguments)
#_update_parser(cmd_parser, instance.arguments)
if '-h' in cmd_args or '--help' in cmd_args:
cmd_parser.print_help()
return
......@@ -144,20 +177,20 @@ class Shell(Cmd):
_print_error_message(err)
elif ('-h' in cmd_args or '--help' in cmd_args) \
or len(cmd_args): # print options
print('%s: %s' % (subname, subcmd.help))
print('%s: %s' % (cmd.name, subcmd.help))
options = {}
for sub in subcmd.get_subcommands():
options[sub.name] = sub.help
print_dict(options)
else: # change context
new_context = self
#new_context = this
backup_context = self._backup()
old_prompt = self.prompt
new_context._roll_command(cmd.parent_path)
new_context.set_prompt(subcmd.path.replace('_', ' '))
newcmds = [subcmd for subcmd in subcmd.get_subcommands()]
for subcmd in newcmds:
new_context._push_in_command(subcmd.path)
new_context._register_command(subcmd.path)
new_context.cmdloop()
self.prompt = old_prompt
#when new context is over, roll back to the old one
......@@ -169,10 +202,10 @@ class Shell(Cmd):
self._register_method(help_method, 'help_%s' % cmd.name)
def complete_method(self, text, line, begidx, endidx):
subcmd, cmd_args = cmd.parse_out(line.split()[1:])
subcmd, cmd_args = cmd.parse_out(split_input(line)[1:])
if subcmd.is_command:
cls = subcmd.get_class()
instance = cls(dict(_arguments))
instance = cls(dict(arguments))
empty, sep, subname = subcmd.path.partition(cmd.path)
cmd_name = '%s %s' % (cmd.name, subname.replace('_', ' '))
print('\n%s\nSyntax:\t%s %s'\
......@@ -192,8 +225,9 @@ class Shell(Cmd):
hdr = tmp_partition[0].strip()
return '%s commands:' % hdr
def run(self, path=''):
self._history = History(_arguments['config'].get('history', 'file'))
def run(self, arguments, path=''):
self._history = History(arguments['config'].get('history', 'file'))
self._arguments = arguments
if path:
cmd = self.cmd_tree.get_command(path)
intro = cmd.path.replace('_', ' ')
......@@ -201,7 +235,7 @@ class Shell(Cmd):
intro = self.cmd_tree.name
for subcmd in self.cmd_tree.get_subcommands(path):
self._push_in_command(subcmd.path)
self._register_command(subcmd.path)
self.set_prompt(intro)
......
......@@ -31,7 +31,7 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.command
from kamaki.cli.new import command
from kamaki.cli import command
from kamaki.clients.astakos import AstakosClient, ClientError
from kamaki.cli.utils import print_dict
from kamaki.cli.errors import raiseCLIError
......
......@@ -37,7 +37,7 @@ from kamaki.cli.commands import _command_init
from kamaki.cli.command_tree import CommandTree
config_cmds = CommandTree('config', 'Configuration commands')
_command = [config_cmds]
_commands = [config_cmds]
@command(config_cmds)
......@@ -77,6 +77,7 @@ class config_set(_command_init):
section = section or 'global'
self.config.set(section, key, value)
self.config.write()
self.config.reload()
@command(config_cmds)
......@@ -88,3 +89,4 @@ class config_delete(_command_init):
section = section or 'global'
self.config.remove_option(section, key)
self.config.write()
self.config.reload()
......@@ -113,14 +113,15 @@ class server_info(_init_cyclades):
def _print(self, server):
addr_dict = {}
if 'attachments' in server:
for addr in server['attachments']['values']:
atts = server.pop('attachments')
for addr in atts['values']:
ips = addr.pop('values', [])
for ip in ips:
addr['IPv%s' % ip['version']] = ip['addr']
if 'firewallProfile' in addr:
addr['firewall'] = addr.pop('firewallProfile')
addr_dict[addr.pop('id')] = addr
server['attachments'] = addr_dict if addr_dict is not {} else None
server['attachments'] = addr_dict if addr_dict else None
if 'metadata' in server:
server['metadata'] = server['metadata']['values']
print_dict(server, ident=2)
......@@ -430,10 +431,13 @@ class server_wait(_init_cyclades):
new_mode = self.client.wait_server(server_id,
currect_status,
wait_cb=wait_cb)
progress_bar.finish()
except KeyboardInterrupt:
print('\nCanceled')
progress_bar.finish()
return
except ClientError as err:
progress_bar.finish()
raiseCLIError(err)
if new_mode:
print('\nServer %s is now in %s mode' % (server_id, new_mode))
......@@ -494,7 +498,7 @@ class network_list(_init_cyclades):
for net in nets:
netname = bold(net.pop('name'))
netid = bold(unicode(net.pop('id')))
print('%s (%s)' % (netname, netid))
print('%s (%s)' % (netid, netname))
if self.get_argument('detail'):
network_info.print_network(net)
......
......@@ -50,18 +50,18 @@ class _init_history(_command_init):
@command(history_cmds)
class history(_init_history):
class history_show(_init_history):
"""Show history [containing terms...]"""
def __init__(self, arguments={}):
super(history, self).__init__(arguments)
super(self.__class__, self).__init__(arguments)
self.arguments['limit'] =\
IntArgument('number of lines to show', '-n', default=0)
self.arguments['match'] =\
ValueArgument('show lines that match all given terms', '--match')
def main(self):
super(history, self).main()
super(self.__class__, self).main()
ret = self.history.get(match_terms=self.get_argument('match'),
limit=self.get_argument('limit'))
print(''.join(ret))
......@@ -72,5 +72,5 @@ class history_clean(_init_history):
"""Clean up history"""
def main(self):
super(history_clean, self).main()
super(self.__class__, self).main()
self.history.clean()
......@@ -133,7 +133,9 @@ class image_test(_init_image):
@command(image_cmds)
class image_register(_init_image):
"""Register an image"""
"""(Re)Register an image
call with --update or without an image name to update image properties
"""
def __init__(self, arguments={}):
super(image_register, self).__init__(arguments)
......@@ -152,8 +154,10 @@ class image_register(_init_image):
self.arguments['is_public'] =\
FlagArgument('mark image as public', '--public')
self.arguments['size'] = IntArgument('set image size', '--size')
self.arguments['update'] = FlagArgument(
'update an existing image properties', '--update')
def main(self, name, location):
def main(self, location, name=None):
super(self.__class__, self).main()
if not location.startswith('pithos://'):
account = self.config.get('store', 'account') \
......@@ -178,11 +182,13 @@ class image_register(_init_image):
if val is not None:
params[key] = val
update = self.get_argument('update')
properties = self.get_argument('properties')
try:
self.client.register(name,
location,
params,
self.get_argument('properties'))
if name and not update:
self.client.register(name, location, params, properties)
else:
self.client.reregister(location, name, params, properties)
except ClientError as err:
raiseCLIError(err)
......
......@@ -502,8 +502,8 @@ class store_append(_store_container_command):
self).main(container___path, path_is_optional=False)
try:
f = open(local_path, 'r')
progress_bar = self.arguments['progress_bar']
try:
progress_bar = self.arguments['progress_bar']
upload_cb = progress_bar.get_generator('Appending blocks')
except Exception:
upload_cb = None
......@@ -511,7 +511,10 @@ class store_append(_store_container_command):
source_file=f,
upload_cb=upload_cb)
except ClientError as err:
progress_bar.finish()
raiseCLIError(err)
finally:
progress_bar.finish()
@command(pithos_cmds)
......@@ -541,8 +544,8 @@ class store_overwrite(_store_container_command):
self).main(container___path, path_is_optional=False)
try:
f = open(local_path, 'r')
progress_bar = self.arguments['progress_bar']
try:
progress_bar = self.arguments['progress_bar']
upload_cb = progress_bar.get_generator('Overwritting blocks')
except Exception:
upload_cb = None
......@@ -552,7 +555,10 @@ class store_overwrite(_store_container_command):
source_file=f,
upload_cb=upload_cb)
except ClientError as err:
progress_bar.finish()
raiseCLIError(err)
finally:
progress_bar.finish()
@command(pithos_cmds)
......@@ -622,13 +628,15 @@ class store_upload(_store_container_command):
remote_path = local_path if self.path is None else self.path
poolsize = self.get_argument('poolsize')
if poolsize is not None:
self.POOL_SIZE = poolsize
self.client.POOL_SIZE = int(poolsize)
params = dict(content_encoding=self.get_argument('content_encoding'),
content_type=self.get_argument('content_type'),
content_disposition=self.get_argument('content_disposition'),
sharing=self.get_argument('sharing'),
public=self.get_argument('public'))
try:
progress_bar = self.arguments['progress_bar']
hash_bar = progress_bar.clone()
with open(local_path) as f:
if self.get_argument('unchunked'):
self.client.upload_object_unchunked(remote_path, f,
......@@ -636,18 +644,24 @@ class store_upload(_store_container_command):
withHashFile=self.get_argument('use_hashes'),
**params)
else:
progress_bar = self.arguments['progress_bar']
hash_cb = progress_bar.get_generator(\
hash_cb = hash_bar.get_generator(\
'Calculating block hashes')
upload_cb = progress_bar.get_generator('Uploading')
self.client.upload_object(remote_path, f,
hash_cb=hash_cb,
upload_cb=upload_cb,
**params)
hash_cb=hash_cb,
upload_cb=upload_cb,
**params)
progress_bar.finish()
hash_bar.finish()
except ClientError as err:
progress_bar.finish()
hash_bar.finish()
raiseCLIError(err)
except IOError as err:
raise CLIError(message='Failed to read form file %s' % local_path,
progress_bar.finish()
hash_bar.finish()
raise CLIError(
message='Failed to read form file %s' % local_path,
importance=2,
details=unicode(err))
print 'Upload completed'
......@@ -731,13 +745,13 @@ class store_download(_store_container_command):
raise CLIError(message='Cannot write to file %s - %s'\
% (local_path, unicode(err)),
importance=1)
progress_bar = self.arguments['progress_bar']
download_cb = progress_bar.get_generator('Downloading')
poolsize = self.get_argument('poolsize')
if poolsize is not None:
self.POOL_SIZE = int(poolsize)
self.client.POOL_SIZE = int(poolsize)
try:
progress_bar = self.arguments['progress_bar']
download_cb = progress_bar.get_generator('Downloading')
self.client.download_object(self.path, out,
download_cb=download_cb,
range=self.get_argument('range'),
......@@ -747,7 +761,9 @@ class store_download(_store_container_command):
if_none_match=self.get_argument('if_none_match'),
if_modified_since=self.get_argument('if_modified_since'),
if_unmodified_since=self.get_argument('if_unmodified_since'))
progress_bar.finish()
except ClientError as err:
progress_bar.finish()
raiseCLIError(err)
except KeyboardInterrupt:
from threading import enumerate as activethreads
......@@ -759,9 +775,13 @@ class store_download(_store_container_command):
stdout.write('.')
except RuntimeError:
continue
progress_bar.finish()
print('\ndownload canceled by user')
if local_path is not None:
print('to resume, re-run with --resume')
except Exception as e:
progress_bar.finish()
raise e
print