]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
tools: Auto complete feature for CLI.
authorAdam Kupczyk <akupczyk@mirantis.com>
Thu, 18 Feb 2016 09:47:56 +0000 (10:47 +0100)
committerAdam Kupczyk <akupczyk@mirantis.com>
Thu, 31 Mar 2016 07:56:12 +0000 (09:56 +0200)
Now logic moved from bash to python.
Not bind to bash yet. Use as 'ceph --comp osd ls'.
Able to fulfill commands and print command line help.
Signed-off-by: Adam Kupczyk <a.kupczyk@mirantis.com>
src/ceph.in
src/pybind/ceph_argparse.py

index 79c226351253e556f436a24a80d474cad7608189..302165a794cfc4fab19946e3cd0affdab97044c1 100755 (executable)
@@ -474,6 +474,7 @@ def new_style_command(parsed_args, cmdargs, target, sigdict, inbuf, verbose):
     return json_command(cluster_handle, target=target, argdict=valid_dict,
                         inbuf=inbuf)
 
+
 def complete(sigdict, args, target):
     """
     Command completion.  Match as much of [args] as possible,
@@ -491,46 +492,38 @@ def complete(sigdict, args, target):
         args = args[2:]
     # look for best match, accumulate possibles in bestcmds
     # (so we can maybe give a more-useful error message)
-    best_match_cnt = 0
-    bestcmds = []
+
+    match_count = 0
+    comps = []
     for cmdtag, cmd in sigdict.iteritems():
         sig = cmd['sig']
-        matched = matchnum(args, sig, partial=True)
-        if (matched > best_match_cnt):
-            if complete_verbose:
-                print("better match: {0} > {1}: {2}:{3} ".format(matched,
-                        best_match_cnt, cmdtag, concise_sig(sig)),  file=sys.stderr)
-            best_match_cnt = matched
-            bestcmds = [{cmdtag:cmd}]
-        elif matched == best_match_cnt:
-            if complete_verbose:
-                print("equal match: {0} > {1}: {2}:{3} ".format(matched,
-                        best_match_cnt, cmdtag, concise_sig(sig)), file=sys.stderr)
-            bestcmds.append({cmdtag:cmd})
-
-    # look through all matching sigs
-    comps = []
-    for cmddict in bestcmds:
-        for cmd in cmddict.itervalues():
-            sig = cmd['sig']
-            # either:
-            #   we match everything fully, so we want the next desc, or
-            #   we match more partially, so we want the partial match
-            fullindex = matchnum(args, sig, partial=False) - 1
-            partindex = matchnum(args, sig, partial=True) - 1
-            if complete_verbose:
-                print('{}: f {} p {} len {}'.format(sig, fullindex, partindex, len(sig)), file=sys.stderr)
-            if fullindex == partindex and fullindex + 1 < len(sig):
-                d = sig[fullindex + 1]
-            else:
-                d = sig[partindex]
-            comps.append(str(d))
-    if complete_verbose:
-        print('\n'.join(comps), file=sys.stderr)
-    print('\n'.join(comps))
+        j = 0
+        # iterate over all arguments, except last one
+        for arg in args[0:-1]:
+            if j > len(sig)-1:
+                # an out of argument definitions
+                break
+            found_match = arg in sig[j].complete(arg)
+            if not found_match and sig[j].req:
+                # no elements that match
+                break
+            if not sig[j].N:
+                j += 1
+        else:
+            # successfully matched all - except last one - arguments
+            if j < len(sig) and len(args) > 0:
+                comps += sig[j].complete(args[-1])
+
+            match_count += 1
+            match_cmd = cmd
 
+    if match_count == 1 and len(comps) == 0:
+        # only one command matched and no hints yet => add help
+        comps = comps + [' ', '#'+match_cmd['help']]
+    print('\n'.join(sorted(set(comps))))
     return 0
 
+
 ###
 # ping a monitor
 ###
@@ -712,9 +705,6 @@ def main():
             file=sys.stderr)
         return 1
 
-    if childargs in [['mon'], ['osd']]:
-        parsed_args.help = True
-
     if parsed_args.help:
         # short default timeout for -h
         if not timeout:
@@ -727,7 +717,9 @@ def main():
         if len(childargs) < 2:
             print('"ping" requires a monitor name as argument: "ping mon.<id>"', file=sys.stderr)
             return 1
-
+    if parsed_args.completion:
+        #for completion let timeout be really small
+        timeout = 3
     try:
         if childargs and childargs[0] == 'ping':
             return ping_monitor(cluster_handle, childargs[1], timeout)
index 7d5f4cfd3da051af49a084edd61d736a74336a78..021c53d8bcd58e08682f8403604970dcdba914f6 100644 (file)
@@ -120,6 +120,9 @@ class CephArgtype(object):
         """
         return '<{0}>'.format(self.__class__.__name__)
 
+    def complete(self, s):
+        return []
+
 
 class CephInt(CephArgtype):
     """
@@ -219,6 +222,12 @@ class CephString(CephArgtype):
             b += '(goodchars {0})'.format(self.goodchars)
         return '<string{0}>'.format(b)
 
+    def complete(self, s):
+        if s == '':
+            return []
+        else:
+            return [s]
+
 
 class CephSocketpath(CephArgtype):
     """
@@ -450,6 +459,10 @@ class CephChoices(CephArgtype):
         else:
             return '{0}'.format('|'.join(self.strings))
 
+    def complete(self, s):
+        all_elems = [token for token in self.strings if token.startswith(s)]
+        return all_elems
+
 
 class CephFilepath(CephArgtype):
     """
@@ -536,6 +549,12 @@ class CephPrefix(CephArgtype):
     def __str__(self):
         return self.prefix
 
+    def complete(self, s):
+        if self.prefix.startswith(s):
+            return [self.prefix.rstrip(' ')]
+        else:
+            return []
+
 
 class argdesc(object):
     """
@@ -618,6 +637,9 @@ class argdesc(object):
             s = '{' + s + '}'
         return s
 
+    def complete(self, s):
+        return self.instance.complete(s)
+
 
 def concise_sig(sig):
     """