[svn] commit: r961 - in /trunk: ./ src/bin/bindctl/ src/bin/bindctl/unittest/ src/bin/cmdctl/ src/bin/cmdctl/unittest/

BIND 10 source code commits bind10-changes at lists.isc.org
Thu Feb 25 08:20:48 UTC 2010


Author: zhanglikun
Date: Thu Feb 25 08:20:48 2010
New Revision: 961

Log:
1. Add unittests and help information for cmdctl and bindctl.
2. Add login idle timeout for cmdctl, default idle time is 1200 seconds.
3. Refactor some code for cmdctl.

Added:
    trunk/src/bin/cmdctl/cmdctl.py.in
      - copied, changed from r960, trunk/src/bin/cmdctl/b10-cmdctl.py.in
    trunk/src/bin/cmdctl/unittest/
    trunk/src/bin/cmdctl/unittest/cmdctl_test.in
    trunk/src/bin/cmdctl/unittest/cmdctl_test.py
Removed:
    trunk/src/bin/cmdctl/b10-cmdctl.py.in
Modified:
    trunk/configure.ac
    trunk/src/bin/bindctl/TODO
    trunk/src/bin/bindctl/bindcmd.py
    trunk/src/bin/bindctl/bindctl.py
    trunk/src/bin/bindctl/cmdparse.py
    trunk/src/bin/bindctl/moduleinfo.py
    trunk/src/bin/bindctl/unittest/bindctl_test.py
    trunk/src/bin/cmdctl/Makefile.am

Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac (original)
+++ trunk/configure.ac Thu Feb 25 08:20:48 2010
@@ -172,8 +172,9 @@
                ])
 AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
            src/bin/cfgmgr/run_b10-cfgmgr.sh
-           src/bin/cmdctl/b10-cmdctl.py
+           src/bin/cmdctl/cmdctl.py
            src/bin/cmdctl/run_b10-cmdctl.sh
+           src/bin/cmdctl/unittest/cmdctl_test
            src/bin/bind10/bind10.py
            src/bin/bind10/bind10_test
            src/bin/bind10/run_bind10.sh
@@ -190,6 +191,8 @@
            chmod +x src/bin/cfgmgr/run_b10-cfgmgr.sh
            chmod +x src/bin/cmdctl/run_b10-cmdctl.sh
            chmod +x src/bin/bind10/run_bind10.sh
+           chmod +x src/bin/cmdctl/unittest/cmdctl_test
+           chmod +x src/bin/bindctl/unittest/bindctl_test
            chmod +x src/bin/bindctl/bindctl
            chmod +x src/bin/msgq/run_msgq.sh
            chmod +x src/bin/msgq/msgq_test

Modified: trunk/src/bin/bindctl/TODO
==============================================================================
--- trunk/src/bin/bindctl/TODO (original)
+++ trunk/src/bin/bindctl/TODO Thu Feb 25 08:20:48 2010
@@ -1,3 +1,15 @@
 1. Refactor the code for bindctl.
 2. Update man page for bindctl provided by jreed.
 3. Add more unit tests.
+4. Need Review:
+        bindcmd.py:
+            apply_config_cmd()
+            _validate_cmd()
+            complete()
+            
+        cmdparse.py:
+            _parse_params
+
+        moduleinfo.py:
+            get_param_name_by_position
+

Modified: trunk/src/bin/bindctl/bindcmd.py
==============================================================================
--- trunk/src/bin/bindctl/bindcmd.py (original)
+++ trunk/src/bin/bindctl/bindcmd.py Thu Feb 25 08:20:48 2010
@@ -61,47 +61,52 @@
         self.modules = OrderedDict()
         self.add_module_info(ModuleInfo("help", desc = "Get help for bindctl"))
         self.server_port = server_port
-        self.connect_to_cmd_ctrld()
+        self._connect_to_cmd_ctrld()
         self.session_id = self._get_session_id()
 
-    def connect_to_cmd_ctrld(self):
+    def _connect_to_cmd_ctrld(self):
+        '''Connect to cmdctl in SSL context. '''
         try:
             self.conn = http.client.HTTPSConnection(self.server_port, cert_file='bindctl.pem')
         except  Exception as e:
-            print(e)
-            print("can't connect to %s, please make sure cmd-ctrld is running" % self.server_port)
+            print(e, "can't connect to %s, please make sure cmd-ctrld is running" %
+                  self.server_port)
 
     def _get_session_id(self):
+        '''Generate one session id for the connection. '''
         rand = os.urandom(16)
         now = time.time()
         ip = socket.gethostbyname(socket.gethostname())
         session_id = sha1(("%s%s%s" %(rand, now, ip)).encode())
-        session_id = session_id.hexdigest()
-        return session_id
+        digest = session_id.hexdigest()
+        return digest
     
     def run(self):
+        '''Parse commands inputted from user and send them to cmdctl. '''
         try:
-            ret = self.login()
-            if not ret:
+            if not self.login_to_cmdctl():
                 return False
 
             # Get all module information from cmd-ctrld
             self.config_data = isc.config.UIModuleCCSession(self)
-            self.update_commands()
+            self._update_commands()
             self.cmdloop()
         except KeyboardInterrupt:
             return True
 
-    def login(self):
+    def login_to_cmdctl(self):
+        '''Login to cmdctl with the username and password inputted 
+        from user. After login sucessfully, the username and password
+        will be saved in 'default_user.csv', when login next time, 
+        username and password saved in 'default_user.csv' will be used
+        first.
+        '''
         csvfile = None
         bsuccess = False
         try:
             csvfile = open('default_user.csv')
             users = csv.reader(csvfile)
             for row in users:
-                if (len(row) < 2):
-                    continue
-
                 param = {'username': row[0], 'password' : row[1]}
                 response = self.send_POST('/login', param)
                 data = response.read().decode()
@@ -120,10 +125,13 @@
                 return True
 
         count = 0
-        csvfile = None
         print("[TEMP MESSAGE]: username :root  password :bind10")
-        while count < 3:
+        while True:
             count = count + 1
+            if count > 3:
+                print("Too many authentication failures")
+                return False
+
             username = input("Username:")
             passwd = getpass.getpass()
             param = {'username': username, 'password' : passwd}
@@ -135,26 +143,23 @@
                 csvfile = open('default_user.csv', 'w')
                 writer = csv.writer(csvfile)
                 writer.writerow([username, passwd])
-                bsuccess = True
-                break
-
-            if count == 3:
-                print("Too many authentication failures")
-                break
-
-        if csvfile:
-            csvfile.close()
-        return bsuccess
-
-    def update_commands(self):
+                csvfile.close()
+                return True
+
+
+    def _update_commands(self):
+        '''Get all commands of modules. '''
         cmd_spec = self.send_GET('/command_spec')
-        if (len(cmd_spec) == 0):
-            print('can\'t get any command specification')
+        if not cmd_spec:
+            return
+
         for module_name in cmd_spec.keys():
-            if cmd_spec[module_name]:
-                self.prepare_module_commands(module_name, cmd_spec[module_name])
+            self._prepare_module_commands(module_name, cmd_spec[module_name])
 
     def send_GET(self, url, body = None):
+        '''Send GET request to cmdctl, session id is send with the name
+        'cookie' in header.
+        '''
         headers = {"cookie" : self.session_id}
         self.conn.request('GET', url, body, headers)
         res = self.conn.getresponse()
@@ -162,11 +167,12 @@
         if reply_msg:
            return json.loads(reply_msg.decode())
         else:
-            return None
+            return {}
        
 
     def send_POST(self, url, post_param = None): 
-        '''
+        '''Send GET request to cmdctl, session id is send with the name
+        'cookie' in header.
         Format: /module_name/command_name
         parameters of command is encoded as a map
         '''
@@ -183,13 +189,12 @@
         self.prompt = self.location + self.prompt_end
         return stop
 
-    def prepare_module_commands(self, module_name, module_commands):
+    def _prepare_module_commands(self, module_name, module_commands):
         module = ModuleInfo(name = module_name,
                             desc = "same here")
         for command in module_commands:
             cmd = CommandInfo(name = command["command_name"],
-                              desc = command["command_description"],
-                              need_inst_param = False)
+                              desc = command["command_description"])
             for arg in command["command_args"]:
                 param = ParamInfo(name = arg["item_name"],
                                   type = arg["item_type"],
@@ -200,7 +205,7 @@
             module.add_command(cmd)
         self.add_module_info(module)
 
-    def validate_cmd(self, cmd):
+    def _validate_cmd(self, cmd):
         if not cmd.module in self.modules:
             raise CmdUnknownModuleSyntaxError(cmd.module)
         
@@ -225,7 +230,6 @@
                                              list(params.keys())[0])
         elif params:
             param_name = None
-            index = 0
             param_count = len(params)
             for name in params:
                 # either the name of the parameter must be known, or
@@ -250,18 +254,17 @@
                             raise CmdUnknownParamSyntaxError(cmd.module, cmd.command, cmd.params[name])
                     else:
                         # replace the numbered items by named items
-                        param_name = command_info.get_param_name_by_position(name+1, index, param_count)
+                        param_name = command_info.get_param_name_by_position(name+1, param_count)
                         cmd.params[param_name] = cmd.params[name]
                         del cmd.params[name]
                         
                 elif not name in all_params:
                     raise CmdUnknownParamSyntaxError(cmd.module, cmd.command, name)
+
             param_nr = 0
             for name in manda_params:
                 if not name in params and not param_nr in params:
                     raise CmdMissParamSyntaxError(cmd.module, cmd.command, name)
-                
-                param_nr += 1
                 param_nr += 1
 
     def _handle_cmd(self, cmd):
@@ -385,7 +388,7 @@
     def _parse_cmd(self, line):
         try:
             cmd = BindCmdParse(line)
-            self.validate_cmd(cmd)
+            self._validate_cmd(cmd)
             self._handle_cmd(cmd)
         except BindCtlException as e:
             print("Error! ", e)
@@ -497,5 +500,3 @@
         print("received reply:", data)
 
 
-
-

Modified: trunk/src/bin/bindctl/bindctl.py
==============================================================================
--- trunk/src/bin/bindctl/bindctl.py (original)
+++ trunk/src/bin/bindctl/bindctl.py Thu Feb 25 08:20:48 2010
@@ -18,69 +18,97 @@
 from bindcmd import *
 import isc
 import pprint
+from optparse import OptionParser, OptionValueError
 
+__version__ = 'Bindctl'
 
 def prepare_config_commands(tool):
     module = ModuleInfo(name = "config", desc = "Configuration commands")
-    cmd = CommandInfo(name = "show", desc = "Show configuration", need_inst_param = False)
+    cmd = CommandInfo(name = "show", desc = "Show configuration")
     param = ParamInfo(name = "identifier", type = "string", optional=True)
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "add", desc = "Add entry to configuration list", need_inst_param = False)
+    cmd = CommandInfo(name = "add", desc = "Add entry to configuration list")
     param = ParamInfo(name = "identifier", type = "string", optional=True)
     cmd.add_param(param)
     param = ParamInfo(name = "value", type = "string", optional=False)
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "remove", desc = "Remove entry from configuration list", need_inst_param = False)
+    cmd = CommandInfo(name = "remove", desc = "Remove entry from configuration list")
     param = ParamInfo(name = "identifier", type = "string", optional=True)
     cmd.add_param(param)
     param = ParamInfo(name = "value", type = "string", optional=False)
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "set", desc = "Set a configuration value", need_inst_param = False)
+    cmd = CommandInfo(name = "set", desc = "Set a configuration value")
     param = ParamInfo(name = "identifier", type = "string", optional=True)
     cmd.add_param(param)
     param = ParamInfo(name = "value", type = "string", optional=False)
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "unset", desc = "Unset a configuration value", need_inst_param = False)
+    cmd = CommandInfo(name = "unset", desc = "Unset a configuration value")
     param = ParamInfo(name = "identifier", type = "string", optional=False)
     cmd.add_param(param)
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "diff", desc = "Show all local changes", need_inst_param = False)
+    cmd = CommandInfo(name = "diff", desc = "Show all local changes")
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "revert", desc = "Revert all local changes", need_inst_param = False)
+    cmd = CommandInfo(name = "revert", desc = "Revert all local changes")
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "commit", desc = "Commit all local changes", need_inst_param = False)
+    cmd = CommandInfo(name = "commit", desc = "Commit all local changes")
     module.add_command(cmd)
 
-    cmd = CommandInfo(name = "go", desc = "Go to a specific configuration part", need_inst_param = False)
+    cmd = CommandInfo(name = "go", desc = "Go to a specific configuration part")
     param = ParamInfo(name = "identifier", type="string", optional=False)
     cmd.add_param(param)
     module.add_command(cmd)
 
     tool.add_module_info(module)
 
-if __name__ == '__main__':
-    tool = BindCmdInterpreter("localhost:8080")
-    prepare_config_commands(tool)
-    tool.run()
-# TODO: put below back, was removed to see errors
-#if __name__ == '__main__':
-    #try:
-        #tool = BindCmdInterpreter("localhost:8080")
-        #prepare_config_commands(tool)
-        #tool.run()
-    #except Exception as e:
-        #print(e)
-        #print("Failed to connect with b10-cmdctl module, is it running?")
+def check_port(option, opt_str, value, parser):
+    if (value < 0) or (value > 65535):
+        raise OptionValueError('%s requires a port number (0-65535)' % opt_str)
+    parser.values.port = value
+
+def check_addr(option, opt_str, value, parser):
+    ipstr = value
+    ip_family = socket.AF_INET
+    if (ipstr.find(':') != -1):
+        ip_family = socket.AF_INET6
+
+    try:
+        socket.inet_pton(ip_family, ipstr)
+    except:
+        raise OptionValueError("%s invalid ip address" % ipstr)
+
+    parser.values.addr = value
+
+def set_bindctl_options(parser):
+    parser.add_option('-p', '--port', dest = 'port', type = 'int',
+            action = 'callback', callback=check_port,
+            default = '8080', help = 'port for cmdctl of bind10')
+
+    parser.add_option('-a', '--address', dest = 'addr', type = 'string',
+            action = 'callback', callback=check_addr,
+            default = '127.0.0.1', help = 'IP address for cmdctl of bind10')
 
 
+if __name__ == '__main__':
+    try:
+        parser = OptionParser(version = __version__)
+        set_bindctl_options(parser)
+        (options, args) = parser.parse_args()
+        server_addr = options.addr + ':' + str(options.port)
+        tool = BindCmdInterpreter(server_addr)
+        prepare_config_commands(tool)
+        tool.run()
+    except Exception as e:
+        print(e, "\nFailed to connect with b10-cmdctl module, is it running?")
+
+

Modified: trunk/src/bin/bindctl/cmdparse.py
==============================================================================
--- trunk/src/bin/bindctl/cmdparse.py (original)
+++ trunk/src/bin/bindctl/cmdparse.py Thu Feb 25 08:20:48 2010
@@ -34,10 +34,10 @@
 NAME_PATTERN = re.compile("^\s*(?P<name>[\w]+)(?P<blank>\s*)(?P<others>.*)$")
 
 class BindCmdParse:
-    """ This class will parse the command line user input into three parts:
-    module name, command, parameters.
-    The first two parts are strings and parameter is one hash.
-    The parameter part is optional.
+    """ This class will parse the command line usr input into three part
+    module name, command, parameters
+    the first two parts are strings and parameter is one hash, 
+    parameters part is optional
     
     Example: zone reload, zone_name=example.com 
     module == zone
@@ -52,6 +52,7 @@
         self._parse_cmd(cmd)
 
     def _parse_cmd(self, text_str):    
+        '''Parse command line. '''
         # Get module name
         groups = NAME_PATTERN.match(text_str)
         if not groups:

Modified: trunk/src/bin/bindctl/moduleinfo.py
==============================================================================
--- trunk/src/bin/bindctl/moduleinfo.py (original)
+++ trunk/src/bin/bindctl/moduleinfo.py Thu Feb 25 08:20:48 2010
@@ -51,10 +51,8 @@
     more parameters
     """
 
-    def __init__(self, name, desc = "", need_inst_param = True):
+    def __init__(self, name, desc = ""):
         self.name = name
-        # Wether command needs parameter "instance_name" 
-        self.need_inst_param = need_inst_param 
         self.desc = desc
         self.params = OrderedDict()        
         # Set default parameter "help"
@@ -91,7 +89,7 @@
         return [name for name in all_names 
                 if not self.params[name].is_optional]        
         
-    def get_param_name_by_position(self, pos, index, param_count):
+    def get_param_name_by_position(self, pos, param_count):
         # count mandatories back from the last
         # from the last mandatory; see the number of mandatories before it
         # and compare that to the number of positional arguments left to do
@@ -101,7 +99,9 @@
         # (can this be done in all cases? this is certainly not the most efficient method;
         # one way to make the whole of this more consistent is to always set mandatories first, but
         # that would make some commands less nice to use ("config set value location" instead of "config set location value")
-        if type(pos) == int:
+        if type(pos) != int:
+            raise KeyError(str(pos) + " is not an integer")
+        else:
             if param_count == len(self.params) - 1:
                 i = 0
                 for k in self.params.keys():
@@ -131,14 +131,9 @@
                     raise KeyError(str(pos) + " out of range")
             else:
                 raise KeyError("Too many parameters")
-        else:
-            raise KeyError(str(pos) + " is not an integer")
-    
 
-    def need_instance_param(self):
-        return self.need_inst_param
 
-    def command_help(self, inst_name, inst_type, inst_desc):
+    def command_help(self):
         print("Command ", self)
         print("\t\thelp (Get help for command)")
                 
@@ -166,65 +161,39 @@
 
 class ModuleInfo:
     """Define the information of one module, include module name, 
-    module supporting commands, instance name and the value type of instance name
+    module supporting commands.
     """    
     
-    def __init__(self, name, inst_name = "", inst_type = STRING_TYPE, 
-                 inst_desc = "", desc = ""):
+    def __init__(self, name, desc = ""):
         self.name = name
-        self.inst_name = inst_name
-        self.inst_type = inst_type
-        self.inst_desc = inst_desc
         self.desc = desc
         self.commands = OrderedDict()         
         self.add_command(CommandInfo(name = "help", 
-                                     desc = "Get help for module",
-                                     need_inst_param = False))
+                                     desc = "Get help for module"))
         
     def __str__(self):
         return str("%s \t%s" % (self.name, self.desc))
         
     def add_command(self, command_info):        
         self.commands[command_info.name] = command_info
-        if command_info.need_instance_param():
-            command_info.add_param(ParamInfo(name = self.inst_name, 
-                                             type = self.inst_type,
-                                             desc = self.inst_desc))
-
         
     def has_command_with_name(self, command_name):
         return command_name in self.commands
         
-
     def get_command_with_name(self, command_name):
         return self.commands[command_name]
-        
         
     def get_commands(self):
         return list(self.commands.values())
         
-    
     def get_command_names(self):
         return list(self.commands.keys())
-        
-    
-    def get_instance_param_name(self):
-        return self.inst_name
-        
-        
-    def get_instance_param_type(self):
-        return self.inst_type
-        
 
     def module_help(self):
         print("Module ", self, "\nAvailable commands:")
         for k in self.commands.keys():
             print("\t", self.commands[k])
             
-            
     def command_help(self, command):
-        self.commands[command].command_help(self.inst_name, 
-                                            self.inst_type,
-                                            self.inst_desc)
-    
+        self.commands[command].command_help()    
 

Modified: trunk/src/bin/bindctl/unittest/bindctl_test.py
==============================================================================
--- trunk/src/bin/bindctl/unittest/bindctl_test.py (original)
+++ trunk/src/bin/bindctl/unittest/bindctl_test.py Thu Feb 25 08:20:48 2010
@@ -85,16 +85,6 @@
         self.my_assert_raise(CmdCommandNameFormatError, "zone z-d ")
         self.my_assert_raise(CmdCommandNameFormatError, "zone zdd/")
         self.my_assert_raise(CmdCommandNameFormatError, "zone zdd/ \"")
-        
-
-    def testCmdParamFormatError(self): 
-        self.my_assert_raise(CmdParamFormatError, "zone load load")
-        self.my_assert_raise(CmdParamFormatError, "zone load load=")
-        self.my_assert_raise(CmdParamFormatError, "zone load load==dd")
-        self.my_assert_raise(CmdParamFormatError, "zone load , zone_name=dd zone_file=d" )
-        self.my_assert_raise(CmdParamFormatError, "zone load zone_name=dd zone_file" )
-        self.my_assert_raise(CmdParamFormatError, "zone zdd \"")
-        
 
 class TestCmdSyntax(unittest.TestCase):
     
@@ -103,18 +93,21 @@
         
         tool = bindcmd.BindCmdInterpreter()        
         zone_file_param = ParamInfo(name = "zone_file")
+        zone_name = ParamInfo(name = 'zone_name')
         load_cmd = CommandInfo(name = "load")
         load_cmd.add_param(zone_file_param)
+        load_cmd.add_param(zone_name)
         
         param_master = ParamInfo(name = "master", optional = True)                                 
         param_allow_update = ParamInfo(name = "allow_update", optional = True)                                           
         set_cmd = CommandInfo(name = "set")
         set_cmd.add_param(param_master)
         set_cmd.add_param(param_allow_update)
+        set_cmd.add_param(zone_name)
         
-        reload_all_cmd = CommandInfo(name = "reload_all", need_inst_param = False)        
+        reload_all_cmd = CommandInfo(name = "reload_all")        
         
-        zone_module = ModuleInfo(name = "zone", inst_name = "zone_name")                             
+        zone_module = ModuleInfo(name = "zone")                             
         zone_module.add_command(load_cmd)
         zone_module.add_command(set_cmd)
         zone_module.add_command(reload_all_cmd)
@@ -129,12 +122,12 @@
         
     def no_assert_raise(self, cmd_line):
         cmd = cmdparse.BindCmdParse(cmd_line)
-        self.bindcmd.validate_cmd(cmd) 
+        self.bindcmd._validate_cmd(cmd) 
         
         
     def my_assert_raise(self, exception_type, cmd_line):
         cmd = cmdparse.BindCmdParse(cmd_line)
-        self.assertRaises(exception_type, self.bindcmd.validate_cmd, cmd)  
+        self.assertRaises(exception_type, self.bindcmd._validate_cmd, cmd)  
         
         
     def testValidateSuccess(self):

Modified: trunk/src/bin/cmdctl/Makefile.am
==============================================================================
--- trunk/src/bin/cmdctl/Makefile.am (original)
+++ trunk/src/bin/cmdctl/Makefile.am Thu Feb 25 08:20:48 2010
@@ -5,10 +5,10 @@
 b10_cmdctldir = $(DESTDIR)$(pkgdatadir)
 b10_cmdctl_DATA = passwd.csv b10-cmdctl.pem
 
-CLEANFILES=	b10-cmdctl
+CLEANFILES=	cmdctl.py
 
 # TODO: does this need $$(DESTDIR) also?
 # this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
-b10-cmdctl: b10-cmdctl.py
-	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" b10-cmdctl.py >$@
+b10-cmdctl: cmdctl.py
+	$(SED) "s|@@PYTHONPATH@@|@pyexecdir@|" cmdctl.py >$@
 	chmod a+x $@




More information about the bind10-changes mailing list