[spp] [PATCH 07/13] controller: refactor shell.py

ogawa.yasufumi at lab.ntt.co.jp ogawa.yasufumi at lab.ntt.co.jp
Tue Mar 6 11:50:49 CET 2018


From: Yasufumi Ogawa <ogawa.yasufumi at lab.ntt.co.jp>

Change loading command plugin from usign setattr() to directly assigning
it to Shell class. It is because Shell object provides completion and
showing help only for class methods and does not for methods added with
setattr() mehtod.

This update also includes misc refactoring of whole of shell.py.

Signed-off-by: Yasufumi Ogawa <ogawa.yasufumi at lab.ntt.co.jp>
---
 src/controller/command/hello.py |   2 +-
 src/controller/shell.py         | 246 +++++++++++++++++++++++-----------------
 2 files changed, 141 insertions(+), 107 deletions(-)

diff --git a/src/controller/command/hello.py b/src/controller/command/hello.py
index f898234..e3d974b 100644
--- a/src/controller/command/hello.py
+++ b/src/controller/command/hello.py
@@ -10,7 +10,7 @@ class Hello(object):
         print("Hello, %s!" % self.name)
 
 
-def do_hello(name):
+def do_hello(self, name):
     """Say hello to given user
 
     spp > hello alice
diff --git a/src/controller/shell.py b/src/controller/shell.py
index 3cb5f96..c5ef82d 100644
--- a/src/controller/shell.py
+++ b/src/controller/shell.py
@@ -28,6 +28,8 @@ class Shell(cmd.Cmd, object):
     SEC_SUBCMDS = ['vhost', 'ring', 'pcap', 'nullpmd']
     BYE_CMDS = ['sec', 'all']
 
+    PLUGIN_DIR = 'command'
+
     def default(self, line):
         """Define defualt behaviour
 
@@ -159,18 +161,6 @@ class Shell(cmd.Cmd, object):
 
         return valid
 
-    def complete_pri(self, text, line, begidx, endidx):
-        """Completion for primary process commands"""
-
-        if not text:
-            completions = self.PRI_CMDS[:]
-        else:
-            completions = [p
-                           for p in self.PRI_CMDS
-                           if p.startswith(text)
-                           ]
-        return completions
-
     def clean_sec_cmd(self, cmdstr):
         """remove unwanted spaces to avoid invalid command error"""
 
@@ -178,64 +168,6 @@ class Shell(cmd.Cmd, object):
         res = re.sub(r'\s?;\s?', ";", tmparg)
         return res
 
-    def complete_sec(self, text, line, begidx, endidx):
-        """Completion for secondary process commands"""
-
-        try:
-            cleaned_line = self.clean_sec_cmd(line)
-            if len(cleaned_line.split()) == 1:
-                completions = [str(i)+";" for i in spp_common.SECONDARY_LIST]
-            elif len(cleaned_line.split()) == 2:
-                if not (";" in cleaned_line):
-                    tmplist = [str(i) for i in spp_common.SECONDARY_LIST]
-                    completions = [p+";"
-                                   for p in tmplist
-                                   if p.startswith(text)
-                                   ]
-                elif cleaned_line[-1] == ";":
-                    completions = self.SEC_CMDS[:]
-                else:
-                    seccmd = cleaned_line.split(";")[1]
-                    if cleaned_line[-1] != " ":
-                        completions = [p
-                                       for p in self.SEC_CMDS
-                                       if p.startswith(seccmd)
-                                       ]
-                    elif ("add" in seccmd) or ("del" in seccmd):
-                        completions = self.SEC_SUBCMDS[:]
-                    else:
-                        completions = []
-            elif len(cleaned_line.split()) == 3:
-                subcmd = cleaned_line.split()[-1]
-                if ("add" == subcmd) or ("del" == subcmd):
-                    completions = self.SEC_SUBCMDS[:]
-                else:
-                    if cleaned_line[-1] == " ":
-                        completions = []
-                    else:
-                        completions = [p
-                                       for p in self.SEC_SUBCMDS
-                                       if p.startswith(subcmd)
-                                       ]
-            else:
-                completions = []
-            return completions
-        except Exception as e:
-            print(len(cleaned_line.split()))
-            print(e)
-
-    def complete_bye(self, text, line, begidx, endidx):
-        """Completion for bye commands"""
-
-        if not text:
-            completions = self.BYE_CMDS[:]
-        else:
-            completions = [p
-                           for p in self.BYE_CMDS
-                           if p.startswith(text)
-                           ]
-        return completions
-
     def response(self, result, message):
         """Enqueue message from other than CLI"""
 
@@ -251,6 +183,28 @@ class Shell(cmd.Cmd, object):
             if logger is not None:
                 logger.debug("unknown remote command = %s" % rcmd)
 
+    def precmd(self, line):
+        """Called before running a command
+
+        It is called for checking a contents of command line.
+        """
+
+        if self.recorded_file:
+            if not (
+                    ('playback' in line) or
+                    ('bye' in line) or
+                    ('exit' in line)):
+                self.recorded_file.write("%s\n" % line)
+        return line
+
+    def close(self):
+        """Close record file"""
+
+        if self.recorded_file:
+            print("closing file")
+            self.recorded_file.close()
+            self.recorded_file = None
+
     def do_status(self, _):
         """Display status info of SPP processes
 
@@ -278,6 +232,18 @@ class Shell(cmd.Cmd, object):
             print(message)
             self.response(self.CMD_ERROR, message)
 
+    def complete_pri(self, text, line, begidx, endidx):
+        """Completion for primary process commands"""
+
+        if not text:
+            completions = self.PRI_CMDS[:]
+        else:
+            completions = [p
+                           for p in self.PRI_CMDS
+                           if p.startswith(text)
+                           ]
+        return completions
+
     def do_sec(self, arg):
         """Send command to secondary process
 
@@ -310,8 +276,51 @@ class Shell(cmd.Cmd, object):
             print ("first %s" % cmds[1])
             self.response(self.CMD_ERROR, "invalid format")
 
-    def complete_record(self, text, line, begidx, endidx):
-        return common.compl_common(text, line)
+    def complete_sec(self, text, line, begidx, endidx):
+        """Completion for secondary process commands"""
+
+        try:
+            cleaned_line = self.clean_sec_cmd(line)
+            if len(cleaned_line.split()) == 1:
+                completions = [str(i)+";" for i in spp_common.SECONDARY_LIST]
+            elif len(cleaned_line.split()) == 2:
+                if not (";" in cleaned_line):
+                    tmplist = [str(i) for i in spp_common.SECONDARY_LIST]
+                    completions = [p+";"
+                                   for p in tmplist
+                                   if p.startswith(text)
+                                   ]
+                elif cleaned_line[-1] == ";":
+                    completions = self.SEC_CMDS[:]
+                else:
+                    seccmd = cleaned_line.split(";")[1]
+                    if cleaned_line[-1] != " ":
+                        completions = [p
+                                       for p in self.SEC_CMDS
+                                       if p.startswith(seccmd)
+                                       ]
+                    elif ("add" in seccmd) or ("del" in seccmd):
+                        completions = self.SEC_SUBCMDS[:]
+                    else:
+                        completions = []
+            elif len(cleaned_line.split()) == 3:
+                subcmd = cleaned_line.split()[-1]
+                if ("add" == subcmd) or ("del" == subcmd):
+                    completions = self.SEC_SUBCMDS[:]
+                else:
+                    if cleaned_line[-1] == " ":
+                        completions = []
+                    else:
+                        completions = [p
+                                       for p in self.SEC_SUBCMDS
+                                       if p.startswith(subcmd)
+                                       ]
+            else:
+                completions = []
+            return completions
+        except Exception as e:
+            print(len(cleaned_line.split()))
+            print(e)
 
     def do_record(self, fname):
         """Save commands to a log file
@@ -330,7 +339,7 @@ class Shell(cmd.Cmd, object):
             self.recorded_file = open(fname, 'w')
             self.response(self.CMD_OK, "record")
 
-    def complete_playback(self, text, line, begidx, endidx):
+    def complete_record(self, text, line, begidx, endidx):
         return common.compl_common(text, line)
 
     def do_playback(self, fname):
@@ -360,27 +369,8 @@ class Shell(cmd.Cmd, object):
                 print(message)
                 self.response(self.CMD_NG, message)
 
-    def precmd(self, line):
-        """Called before running a command
-
-        It is called for checking a contents of command line.
-        """
-
-        if self.recorded_file:
-            if not (
-                    ('playback' in line) or
-                    ('bye' in line) or
-                    ('exit' in line)):
-                self.recorded_file.write("%s\n" % line)
-        return line
-
-    def close(self):
-        """Close record file"""
-
-        if self.recorded_file:
-            print("closing file")
-            self.recorded_file.close()
-            self.recorded_file = None
+    def complete_playback(self, text, line, begidx, endidx):
+        return common.compl_common(text, line)
 
     def do_pwd(self, args):
         """Show corrent directory
@@ -392,9 +382,6 @@ class Shell(cmd.Cmd, object):
 
         print(os.getcwd())
 
-    def complete_ls(self, text, line, begidx, endidx):
-        return common.compl_common(text, line)
-
     def do_ls(self, args):
         """Show a list of specified directory
 
@@ -409,8 +396,8 @@ class Shell(cmd.Cmd, object):
         else:
             print("No such a directory.")
 
-    def complete_cd(self, text, line, begidx, endidx):
-        return common.compl_common(text, line, 'directory')
+    def complete_ls(self, text, line, begidx, endidx):
+        return common.compl_common(text, line)
 
     def do_cd(self, args):
         """Change current directory
@@ -424,8 +411,8 @@ class Shell(cmd.Cmd, object):
         else:
             print("No such a directory.")
 
-    def complete_mkdir(self, text, line, begidx, endidx):
-        return common.compl_common(text, line)
+    def complete_cd(self, text, line, begidx, endidx):
+        return common.compl_common(text, line, 'directory')
 
     def do_mkdir(self, args):
         """Create a new directory
@@ -438,6 +425,9 @@ class Shell(cmd.Cmd, object):
         c = 'mkdir -p %s' % args
         subprocess.call(c, shell=True)
 
+    def complete_mkdir(self, text, line, begidx, endidx):
+        return common.compl_common(text, line)
+
     def do_bye(self, arg):
         """Terminate SPP processes and controller
 
@@ -464,6 +454,18 @@ class Shell(cmd.Cmd, object):
             self.close()
             return True
 
+    def complete_bye(self, text, line, begidx, endidx):
+        """Completion for bye commands"""
+
+        if not text:
+            completions = self.BYE_CMDS[:]
+        else:
+            completions = [p
+                           for p in self.BYE_CMDS
+                           if p.startswith(text)
+                           ]
+        return completions
+
     def do_exit(self, args):
         """Terminate SPP controller
 
@@ -475,7 +477,13 @@ class Shell(cmd.Cmd, object):
         print('Thank you for using Soft Patch Panel')
         return True
 
-    def do_load(self, args):
+    def do_inspect(self, args):
+        from pprint import pprint
+        if args == '':
+            pprint(vars(self))
+            pprint(self.__class__.__name__)
+
+    def do_load_cmd(self, args):
         """Load command plugin
 
         Path of plugin file is 'spp/src/controller/command'.
@@ -488,9 +496,35 @@ class Shell(cmd.Cmd, object):
         list_args = args.split(' ')
 
         libdir = 'command'
-        loaded = '%s.%s' % (libdir, list_args[0])
-        # importlib.import_module(loaded)
+        mod_name = list_args[0]
+        method_name = 'do_%s' % mod_name
+        loaded = '%s.%s' % (libdir, mod_name)
         exec('import %s' % loaded)
-        do_cmd = '%s.do_%s' % (loaded, list_args[0])
-        setattr(self, 'do_%s' % list_args[0], eval(do_cmd))
+        do_cmd = '%s.%s' % (loaded, method_name)
+        exec('Shell.%s = %s' % (method_name, do_cmd))
+
         print("Module '%s' loaded." % loaded)
+
+    def complete_load_cmd(self, text, line, begidx, endidx):
+        """Complete command plugins
+
+        Search under PLUGIN_DIR with compl_common() method.
+        This method is intended to be used for searching current
+        directory, but not in this case. If text is not '',
+        compl_common() does not work correctly and do filtering
+        for the result by self.
+        """
+
+        curdir = os.path.dirname(__file__)
+        res = common.compl_common(
+            '', '%s/%s' % (curdir, self.PLUGIN_DIR), 'py')
+
+        completions = []
+        for t in res:
+            if text == '':
+                if t[:2] != '__':
+                    completions.append(t[:-3])
+            else:
+                if t[:len(text)] == text:
+                    completions.append(t[:-3])
+        return completions
-- 
2.13.1



More information about the spp mailing list