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

Reply via email to