X-Git-Url: https://code.grnet.gr/git/kamaki/blobdiff_plain/53254b46d84afd1ed39ce63f0ea5919b67af897f..feature-input-output-encoding:/kamaki/cli/command_shell.py diff --git a/kamaki/cli/command_shell.py b/kamaki/cli/command_shell.py index a6b5fe1..b9391bc 100644 --- a/kamaki/cli/command_shell.py +++ b/kamaki/cli/command_shell.py @@ -1,4 +1,4 @@ -# Copyright 2012 GRNET S.A. All rights reserved. +# Copyright 2012-2014 GRNET S.A. All rights reserved. # # Redistribution and use in source and binary forms, with or # without modification, are permitted provided that the following @@ -35,24 +35,23 @@ from cmd import Cmd from os import popen from sys import stdout -from kamaki.cli import _exec_cmd, _print_error_message +from kamaki.cli import exec_cmd, print_error_message, print_subcommands_help from kamaki.cli.argument import ArgumentParseManager -from kamaki.cli.utils import print_dict, split_input, print_items +from kamaki.cli.utils import print_dict, split_input, pref_enc from kamaki.cli.history import History from kamaki.cli.errors import CLIError +from kamaki.clients import ClientError +from kamaki.cli.logger import add_file_logger +log = add_file_logger(__name__) -def _init_shell(exe_string, parser): + +def _init_shell(exe_string, parser, username='', userid=''): parser.arguments.pop('version', None) - parser.arguments.pop('options', None) - parser.arguments.pop('debug', None) - parser.arguments.pop('verbose', None) - parser.arguments.pop('include', None) - parser.arguments.pop('silent', None) shell = Shell() shell.set_prompt(exe_string) from kamaki import __version__ as version - shell.greet(version) + shell.greet(version, username, userid) shell.do_EOF = shell.do_exit from kamaki.cli.command_tree import CommandTree shell.cmd_tree = CommandTree( @@ -69,9 +68,14 @@ class Shell(Cmd): _context_stack = [] _prompt_stack = [] _parser = None + auth_base = None + cloud = None undoc_header = 'interactive shell commands:' + def emptyline(self): + self.lastcmd = '' + def postcmd(self, post, line): if self._context_stack: self._roll_command() @@ -83,8 +87,8 @@ class Shell(Cmd): def precmd(self, line): if line.startswith('/'): - cur_cmd_path = self.prompt.replace(' ', - '_')[len(self._prefix):-len(self._suffix)] + start, end = len(self._prefix), -len(self._suffix) + cur_cmd_path = self.prompt.replace(' ', '_')[start:end] if cur_cmd_path != self.cmd_tree.name: cur_cmd = self.cmd_tree.get_command(cur_cmd_path) self._context_stack.append(self._backup()) @@ -97,17 +101,33 @@ class Shell(Cmd): return line[1:] return line - def greet(self, version): - print('kamaki v%s - Interactive Shell\n\t(exit or ^D to exit)\n'\ - % version) + def greet(self, version, username='', userid=''): + print('kamaki v%s - Interactive Shell\n' % version) + print('\t/exit \tterminate kamaki') + print('\texit or ^D\texit context') + print('\t? or help \tavailable commands') + print('\t?command \thelp on command') + print('\t!\texecute OS shell command') + print('') + if username or userid: + print('Session user is %s (uuid: %s)' % (username, userid)) def set_prompt(self, new_prompt): self.prompt = '%s%s%s' % (self._prefix, new_prompt, self._suffix) + def cmdloop(self): + while True: + try: + Cmd.cmdloop(self) + except KeyboardInterrupt: + print(' - interrupted') + continue + break + def do_exit(self, line): print('') - if self.prompt[len(self._prefix):-len(self._suffix)]\ - == self.cmd_tree.name: + start, end = len(self._prefix), -len(self._suffix) + if self.prompt[start:end] == self.cmd_tree.name: exit(0) return True @@ -133,7 +153,7 @@ class Shell(Cmd): pass def _roll_command(self, cmd_path=None): - for subname in self.cmd_tree.get_subnames(cmd_path): + for subname in self.cmd_tree.subnames(cmd_path): self._unregister_method('do_%s' % subname) self._unregister_method('complete_%s' % subname) self._unregister_method('help_%s' % subname) @@ -146,6 +166,20 @@ class Shell(Cmd): def _restore(self, oldcontext): self.__dict__ = oldcontext + @staticmethod + def _create_help_method(cmd_name, args, required, descr, syntax): + tmp_args = dict(args) + #tmp_args.pop('options', None) + tmp_args.pop('cloud', None) + tmp_args.pop('debug', None) + tmp_args.pop('verbose', None) + tmp_args.pop('silent', None) + tmp_args.pop('config', None) + help_parser = ArgumentParseManager( + cmd_name, tmp_args, required, + syntax=syntax, description=descr, check_required=False) + return help_parser.print_help + def _register_command(self, cmd_path): cmd = self.cmd_tree.get_command(cmd_path) arguments = self._parser.arguments @@ -156,52 +190,64 @@ class Shell(Cmd): is always parsed to most specific even if cmd_term_term is not a terminal path """ + line = line.decode(pref_enc) subcmd, cmd_args = cmd.parse_out(split_input(line)) self._history.add(' '.join([cmd.path.replace('_', ' '), line])) cmd_parser = ArgumentParseManager( - cmd.name, - dict(self._parser.arguments)) - + cmd.name, dict(self._parser.arguments)) cmd_parser.parser.description = subcmd.help # exec command or change context if subcmd.is_command: # exec command - cls = subcmd.get_class() - instance = cls(dict(cmd_parser.arguments)) - cmd_parser.update_arguments(instance.arguments) - instance.arguments.pop('config') - cmd_parser = ArgumentParseManager(subcmd.path, - instance.arguments) - cmd_parser.syntax = '%s %s' % ( - subcmd.path.replace('_', ' '), cls.syntax) - if '-h' in cmd_args or '--help' in cmd_args: - cmd_parser.parser.print_help() - print('\n%s' % subcmd.help) - return - cmd_parser.parse(cmd_args) - - for name, arg in instance.arguments.items(): - arg.value = getattr(cmd_parser.parsed, name, arg.default) try: - _exec_cmd(instance, - cmd_parser.unparsed, - cmd_parser.parser.print_help) - except CLIError as err: - _print_error_message(err) - elif ('-h' in cmd_args or '--help' in cmd_args) \ - or len(cmd_args): # print options - print('%s: %s' % (cmd.name, subcmd.help)) - options = {} - for sub in subcmd.get_subcommands(): - options[sub.name] = sub.help - print_dict(options) + cls = subcmd.cmd_class + cmd_parser.required = getattr(cls, 'required', None) + ldescr = getattr(cls, 'long_description', '') + if subcmd.path == 'history_run': + instance = cls( + dict(cmd_parser.arguments), self.auth_base, + cmd_tree=self.cmd_tree) + else: + instance = cls( + dict(cmd_parser.arguments), + self.auth_base, self.cloud) + cmd_parser.update_arguments(instance.arguments) + cmd_parser.arguments = instance.arguments + subpath = subcmd.path.split('_')[ + (len(cmd.path.split('_')) - 1):] + cmd_parser.syntax = '%s %s' % ( + ' '.join(subpath), instance.syntax) + help_method = self._create_help_method( + cmd.name, cmd_parser.arguments, + cmd_parser.required, subcmd.help, cmd_parser.syntax) + if '-h' in cmd_args or '--help' in cmd_args: + help_method() + if ldescr.strip(): + print('\nDetails:') + print('%s' % ldescr) + return + cmd_parser.parse(cmd_args) + + for name, arg in instance.arguments.items(): + arg.value = getattr( + cmd_parser.parsed, name, arg.default) + + exec_cmd(instance, cmd_parser.unparsed, help_method) + #[term for term in cmd_parser.unparsed\ + # if not term.startswith('-')], + except (ClientError, CLIError) as err: + print_error_message(err) + elif ('-h' in cmd_args or '--help' in cmd_args) or len(cmd_args): + # print options + print('%s' % cmd.help) + print_subcommands_help(cmd) else: # change context #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()] + newcmds = [subcmd for subcmd in subcmd.subcommands.values()] for subcmd in newcmds: new_context._register_command(subcmd.path) new_context.cmdloop() @@ -213,12 +259,15 @@ class Shell(Cmd): def help_method(self): print('%s (%s -h for more options)' % (cmd.help, cmd.name)) if cmd.is_command: - cls = cmd.get_class() + cls = cmd.cmd_class + ldescr = getattr(cls, 'long_description', '') #_construct_command_syntax(cls) plist = self.prompt[len(self._prefix):-len(self._suffix)] plist = plist.split(' ') clist = cmd.path.split('_') upto = 0 + if ldescr: + print('%s' % ldescr) for i, term in enumerate(plist): try: if clist[i] == term: @@ -226,29 +275,26 @@ class Shell(Cmd): except IndexError: break print('Syntax: %s %s' % (' '.join(clist[upto:]), cls.syntax)) - else: - options = dict(name='Options:') - for sub in cmd.get_subcommands(): - options[sub.name] = sub.help - print_items([options]) + if cmd.subcommands: + print_subcommands_help(cmd) self._register_method(help_method, 'help_%s' % cmd.name) def complete_method(self, text, line, begidx, endidx): subcmd, cmd_args = cmd.parse_out(split_input(line)[1:]) if subcmd.is_command: - cls = subcmd.get_class() + cls = subcmd.cmd_class 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'\ - % (cls.description, cmd_name, cls.syntax)) + print('\n%s\nSyntax:\t%s %s' % ( + cls.help, cmd_name, cls.syntax)) cmd_args = {} for arg in instance.arguments.values(): cmd_args[','.join(arg.parsed_name)] = arg.help - print_dict(cmd_args, ident=2) + print_dict(cmd_args, indent=2) stdout.write('%s %s' % (self.prompt, line)) - return subcmd.get_subnames() + return subcmd.subnames() self._register_method(complete_method, 'complete_%s' % cmd.name) @property @@ -258,16 +304,23 @@ class Shell(Cmd): hdr = tmp_partition[0].strip() return '%s commands:' % hdr - def run(self, parser, path=''): + def run(self, auth_base, cloud, parser, path=''): + self.auth_base = auth_base + self.cloud = cloud self._parser = parser - self._history = History( - parser.arguments['config'].get('history', 'file')) + cnf = parser.arguments['config'] + self._history = History(cnf.get('global', 'history_file')) + self._history.limit = cnf.get('global', 'history_limit') if path: cmd = self.cmd_tree.get_command(path) intro = cmd.path.replace('_', ' ') else: intro = self.cmd_tree.name + acceptable = parser.arguments['config'].groups + total = self.cmd_tree.groups.keys() + self.cmd_tree.exclude(set(total).difference(acceptable)) + for subcmd in self.cmd_tree.get_subcommands(path): self._register_command(subcmd.path) @@ -275,6 +328,7 @@ class Shell(Cmd): try: self.cmdloop() - except Exception: + except Exception as e: + print('(%s)' % e) from traceback import print_stack print_stack()