Michael Pasternak has uploaded a new change for review. Change subject: cli: add username/password prompt/command-line functionality ......................................................................
cli: add username/password prompt/command-line functionality Change-Id: I7f468af34955ef37ce975d61519d8b3959dc7a0f Signed-off-by: Michael Pasternak <[email protected]> --- M src/cli/context.py M src/cli/settings.py M src/ovirtcli/context.py M src/ovirtcli/main.py M src/ovirtcli/options.py M src/ovirtcli/settings.py 6 files changed, 158 insertions(+), 20 deletions(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine-cli refs/changes/35/8035/1 diff --git a/src/cli/context.py b/src/cli/context.py index 5802418..3f34d1e 100644 --- a/src/cli/context.py +++ b/src/cli/context.py @@ -32,6 +32,7 @@ import cStringIO from kitchen.text.converters import getwriter from cli.executionmode import ExecutionMode +import getpass class ExecutionContext(object): @@ -52,10 +53,11 @@ welcome = None goodbye = None - def __init__(self, cmdin=None): + def __init__(self, cmdin=None, args=None): """Constructor.""" self.parameters_cash = {} self.cmdin = cmdin or sys.stdin + self.args = args self.commands = [] self.status = None self.interactive = sys.stdin.isatty() and sys.stdout.isatty() \ @@ -64,7 +66,7 @@ self.parser = create(self.Parser) self.terminal = create(self.Terminal) self._setup_logging() -# self._load_settings() + self._load_settings() self.setup_commands() self.mode = ExecutionMode.SHELL @@ -78,11 +80,27 @@ logger.setLevel(logging.INFO) self._logger = logger + def __collect_connection_data(self): + if self.settings['ovirt-shell:url'] == '': + self.settings['ovirt-shell:url'] = raw_input('URL: ') + if self.settings['ovirt-shell:username'] == '': + self.settings['ovirt-shell:username'] = raw_input('Username: ') + if self.settings['ovirt-shell:password'] == '': + self.settings['ovirt-shell:password'] = getpass.getpass() + sys.stdin.flush() + + def __is_auto_connect(self): + return self.args and ('-c' in self.args or '--connect' in self.args) + def _load_settings(self): """Load settings.""" - found = self.settings.load_config_file() + found, old_format = self.settings.load_config_file() if not found: self.settings.write_example_config_file() + elif old_format: + self.settings.write_config_file() + if self.__is_auto_connect(): + self.__collect_connection_data() self.settings.add_callback('cli:debug', self._set_debug) self._set_debug('cli:debug', self.settings['cli:debug']) diff --git a/src/cli/settings.py b/src/cli/settings.py index f1de602..384e67d 100644 --- a/src/cli/settings.py +++ b/src/cli/settings.py @@ -17,6 +17,7 @@ import os import re +import stat from fnmatch import fnmatch from ConfigParser import ConfigParser @@ -100,17 +101,27 @@ if d is not None and '*' not in p)) def load_config_file(self): - """Load default values from a configuration file.""" + """ + Load default values from a configuration file. + + @return: if-file-exist, is-config-file-in-old-format + """ + old_format = False fname = platform.local_settings_file(self.name) + from ovirtcli.settings import OvirtCliSettings if fname is None: - return False + return False, old_format cp = ConfigParser() if not cp.read(fname): - return False + return False, old_format for section in cp.sections(): for key, value in cp.items(section): - self['%s:%s' % (section, key)] = value - return True + conf_key = '%s:%s' % (section, key) + self[conf_key] = value + if conf_key not in OvirtCliSettings.config_items: + old_format = True + + return True, old_format def _write_config_file(self, settings, example=False): """Overwrite the configuration file with the current settings.""" @@ -120,20 +131,34 @@ ftmp = '%s.%d-tmp' % (fname, os.getpid()) fout = file(ftmp, 'w') sections = {} + from ovirtcli.settings import OvirtCliSettings for key in settings: - section, name = key.split(':') - if section not in sections: - sections[section] = {} - sections[section][name] = settings[key] + if key in OvirtCliSettings.config_items: + section, name = key.split(':') + if section not in sections: + sections[section] = {} + sections[section][name] = settings[key] for section in sorted(sections): fout.write('[%s]\n' % section) - for key in sorted(sections[section]): + for key in sections[section]: if example: fout.write('#') fout.write('%s = %s\n' % (key, sections[section][key])) fout.close() + self.set_file_permissions(ftmp) os.rename(ftmp, fname) + def set_file_permissions(self, f): + #Set UID bit + #Owner has read permission + #Owner has write permission + #Do not dump the file. + os.chmod(f, + stat.S_ISGID | + stat.S_IRUSR | + stat.S_IWUSR | + stat.UF_NODUMP) + def write_config_file(self): """Overwrite the config file with the current settings.""" self._write_config_file(self, False) diff --git a/src/ovirtcli/context.py b/src/ovirtcli/context.py index b0446fe..fd8de8c 100644 --- a/src/ovirtcli/context.py +++ b/src/ovirtcli/context.py @@ -41,8 +41,8 @@ CLI_MODULE_NAME = "ovirt-shell" DEFAULT_VERSION = (0, 0, 0, 0) - def __init__(self): - super(OvirtCliExecutionContext, self).__init__() + def __init__(self, args=None): + super(OvirtCliExecutionContext, self).__init__(args=args) self.connection = None self.formatter = create(Formatter, self.settings['ovirt-shell:output_format']) self.settings.add_callback('cli:verbosity', self._set_verbosity) diff --git a/src/ovirtcli/main.py b/src/ovirtcli/main.py index b754cb0..0abc315 100644 --- a/src/ovirtcli/main.py +++ b/src/ovirtcli/main.py @@ -24,7 +24,7 @@ ############################## MAIN ################################# def main(): parser = create(OvirtCliOptionParser) - context = create(OvirtCliExecutionContext) + context = OvirtCliExecutionContext(sys.argv) shell = EngineShell(context, parser) if len(sys.argv) > 1: diff --git a/src/ovirtcli/options.py b/src/ovirtcli/options.py index c5ba821..22c3ac2 100644 --- a/src/ovirtcli/options.py +++ b/src/ovirtcli/options.py @@ -15,9 +15,9 @@ # -import textwrap -from optparse import OptionParser import sys +import textwrap +from optparse import OptionParser, BadOptionError, AmbiguousOptionError from ovirtcli import settings @@ -37,7 +37,6 @@ self.add_option('-l', '--url', help='specifies the API entry point URL (http[s]://server[:port]/api)') self.add_option('-u', '--username', help='connect as this user') - self.add_option('-p', '--password', help='specify password') self.add_option('-K', '--key-file', help='specify client PEM key-file') self.add_option('-C', '--cert-file', help='specify client PEM cert-file') self.add_option('-A', '--ca-file', help='specify server CA cert-file') @@ -45,7 +44,8 @@ action='store_true') self.add_option('-F', '--filter', help='enables user permission based filtering', action='store_true') - self.add_option('-P', '--port', help='specify port') + #disabled due to bug in httplib + #self.add_option('-P', '--port', help='specify port') self.add_option('-T', '--timeout', help='specify timeout') self.add_option('-c', '--connect', action='store_true', help='automatically connect') @@ -53,7 +53,88 @@ help='read commands from FILE instead of stdin') self.disable_interspersed_args() + # list of hidden app. options (long format) + self.app_options = ['--password'] + def exit(self, status=0, msg=None): self.values._exit = True if msg: print (msg + 'see help for more details.\n') sys.exit(status) + + def _match_long_opt(self, opt): + """_match_long_opt(opt : string) -> string + + Determine which long option string 'opt' matches, ie. which one + it is an unambiguous abbrevation for. Raises BadOptionError if + 'opt' doesn't unambiguously match any long option string. + """ + return self._match_abbrev(opt, self._long_opt) + + def _match_abbrev(self, s, wordmap): + """_match_abbrev(s : string, wordmap : {string : Option}) -> string + + Return the string key in 'wordmap' for which 's' is an unambiguous + abbreviation. If 's' is found to be ambiguous or doesn't match any of + 'words', raise BadOptionError. + """ + + # Is there an exact match? + if s in wordmap: + return s + else: + # Isolate all words with s as a prefix. + option_keys = wordmap.keys() + for item in self.app_options: + if item not in option_keys: + option_keys.append(item) + possibilities = [word for word in option_keys + if word.startswith(s)] + # No exact match, so there had better be just one possibility. + if len(possibilities) == 1: + return possibilities[0] + elif not possibilities: + raise BadOptionError(s) + else: + # More than one possible completion: ambiguous prefix. + possibilities.sort() + raise AmbiguousOptionError(s, possibilities) + + def _process_long_opt(self, rargs, values): + arg = rargs.pop(0) + + # Value explicitly attached to arg? Pretend it's the next + # argument. + if "=" in arg: + (opt, next_arg) = arg.split("=", 1) + rargs.insert(0, next_arg) + had_explicit_value = True + else: + opt = arg + had_explicit_value = False + + opt = self._match_long_opt(opt) + if opt not in self._long_opt.keys(): + # This is app. option (long format) + self.add_option('', opt, help='private app. option') + option = self._long_opt[opt] + if option.takes_value(): + nargs = option.nargs + if len(rargs) < nargs: + if nargs == 1: + self.error(_("%s option requires an argument") % opt) + else: + self.error(_("%s option requires %d arguments") + % (opt, nargs)) + elif nargs == 1: + value = rargs.pop(0) + else: + value = tuple(rargs[0:nargs]) + del rargs[0:nargs] + + elif had_explicit_value: + self.error(_("%s option does not take a value") % opt) + + else: + value = None + + option.process(opt, value, values, self) diff --git a/src/ovirtcli/settings.py b/src/ovirtcli/settings.py index 687bb19..3041e7a 100644 --- a/src/ovirtcli/settings.py +++ b/src/ovirtcli/settings.py @@ -71,3 +71,17 @@ ('ovirt-shell:version', str, ''), ('ovirt-shell:prompt', str, '') ] + + #config file white list + config_items = [ + 'ovirt-shell:url', + 'ovirt-shell:username', + 'ovirt-shell:password', + 'ovirt-shell:key_file', + 'ovirt-shell:cert_file', + 'ovirt-shell:ca_file', + 'ovirt-shell:insecure', + 'ovirt-shell:filter', + 'ovirt-shell:timeout', + 'cli:debug' + ] -- To view, visit http://gerrit.ovirt.org/8035 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I7f468af34955ef37ce975d61519d8b3959dc7a0f Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine-cli Gerrit-Branch: master Gerrit-Owner: Michael Pasternak <[email protected]> _______________________________________________ Engine-patches mailing list [email protected] http://lists.ovirt.org/mailman/listinfo/engine-patches
