Browse Source

Move files from /src/lib/bindctl to /src/bin/bindctl.

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/parkinglot@608 e5f2f494-b856-4b98-b285-d166d9295462
Likun Zhang 15 years ago
parent
commit
ed538af714

+ 1 - 0
configure.ac

@@ -169,6 +169,7 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr
            src/bin/bind10/bind10
            src/bin/bind10/bind10_test
            src/bin/bindctl/bindctl
+           src/bin/bindctl/unittest/bindctl_test
            src/bin/msgq/msgq
            src/bin/msgq/msgq_test
            src/bin/parkinglot/config.h

+ 2 - 0
src/bin/bindctl/README

@@ -0,0 +1,2 @@
+1. Start bindctl by run command "sh bindctl".
+2. Login to b10-cmdctld with the username and password. The username and password will be saved in file default_user.csv automatcally after logining successfully, so next time bindctl will login with the username and password saved in default_user.csv. For more usage information, please turn to "man bindctl".

+ 1 - 0
src/bin/bindctl/__init__.py

@@ -0,0 +1 @@
+__all__ = ['mycollections', 'exception', 'moduleinfo', 'command', 'bindctl']

+ 486 - 0
src/bin/bindctl/bindcmd.py

@@ -0,0 +1,486 @@
+# Copyright (C) 2009  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import sys
+import readline
+from cmd import Cmd
+from exception import *
+from moduleinfo import *
+from cmdparse import BindCmdParse
+from xml.dom import minidom
+import ISC
+import ISC.CC.data
+import http.client
+import json
+import inspect
+import pprint
+import ssl, socket
+import os, time, random, re
+import getpass
+from hashlib import sha1
+import csv
+
+try:
+    from collections import OrderedDict
+except ImportError:
+    from mycollections import OrderedDict
+
+
+CONST_BINDCTL_HELP = """BindCtl, verstion 0.1
+usage: <module name> <command name> [param1 = value1 [, param2 = value2]]
+Type Tab character to get the hint of module/command/paramters.
+Type \"help(? h)\" for help on bindctl.
+Type \"<module_name> help\" for help on the specific module.
+Type \"<module_name> <command_name> help\" for help on the specific command.
+\nAvailable module names: """
+
+CONST_COMMAND_NODE = "command"
+
+class BindCmdInterpreter(Cmd):
+    """simple bindctl example."""    
+
+    def __init__(self, server_port = 'localhost:8080'):
+        Cmd.__init__(self)
+        self.location = ""
+        self.prompt_end = '> '
+        self.prompt = self.prompt_end
+        self.ruler = '-'
+        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.session_id = self._get_session_id()
+
+    def connect_to_cmd_ctrld(self):
+        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)
+
+    def _get_session_id(self):
+        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
+    
+    def run(self):
+        try:
+            ret = self.login()
+            if not ret:
+                return False
+
+            # Get all module information from cmd-ctrld
+            self.config_data = ISC.CC.data.UIConfigData(self)
+            self.update_commands()
+            self.cmdloop()
+        except KeyboardInterrupt:
+            return True
+
+    def login(self):
+        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()
+                if response.status == http.client.OK:
+                    print(data + ' login as ' + row[0] )
+                    bsuccess = True
+                    break
+        except Exception as e:
+            print(e)
+        finally:
+            if csvfile:
+                csvfile.close()
+            if bsuccess:
+                return True
+
+        count = 0
+        csvfile = None
+        print("[TEMP MESSAGE]: username :root  password :bind10")
+        while count < 3:
+            count = count + 1
+            username = input("Username:")
+            passwd = getpass.getpass()
+            param = {'username': username, 'password' : passwd}
+            response = self.send_POST('/login', param)
+            data = response.read().decode()
+            print(data)
+            
+            if response.status == http.client.OK:
+                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):
+        cmd_spec = self.send_GET('/command_spec')
+        if (len(cmd_spec) == 0):
+            print('can\'t get any command specification')
+        for module_name in cmd_spec.keys():
+            self.prepare_module_commands(module_name, cmd_spec[module_name])
+
+    def send_GET(self, url, body = None):
+        headers = {"cookie" : self.session_id}
+        self.conn.request('GET', url, body, headers)
+        res = self.conn.getresponse()
+        reply_msg = res.read()
+        if reply_msg:
+           return json.loads(reply_msg.decode())
+        else:
+            return None
+       
+
+    def send_POST(self, url, post_param = None): 
+        '''
+        Format: /module_name/command_name
+        parameters of command is encoded as a map
+        '''
+        param = None
+        if (len(post_param) != 0):
+            param = json.dumps(post_param)
+
+        headers = {"cookie" : self.session_id}
+        self.conn.request('POST', url, param, headers)
+        return self.conn.getresponse()
+        
+
+    def postcmd(self, stop, line):
+        self.prompt = self.location + self.prompt_end
+        return stop
+
+    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)
+            for arg in command["command_args"]:
+                param = ParamInfo(name = arg["item_name"],
+                                  type = arg["item_type"],
+                                  optional = bool(arg["item_optional"]))
+                if ("item_default" in arg):
+                    param.default = arg["item_default"]
+                cmd.add_param(param)
+            module.add_command(cmd)
+        self.add_module_info(module)
+
+    def validate_cmd(self, cmd):
+        if not cmd.module in self.modules:
+            raise CmdUnknownModuleSyntaxError(cmd.module)
+        
+        module_info = self.modules[cmd.module]
+        if not module_info.has_command_with_name(cmd.command):
+            raise CmdUnknownCmdSyntaxError(cmd.module, cmd.command)
+
+        command_info = module_info.get_command_with_name(cmd.command)
+        manda_params = command_info.get_mandatory_param_names()
+        all_params = command_info.get_param_names()
+        
+        # If help is inputed, don't do further paramters validation.
+        for val in cmd.params.keys():
+            if val == "help":
+                return
+        
+        params = cmd.params.copy()       
+        if not params and manda_params:            
+            raise CmdMissParamSyntaxError(cmd.module, cmd.command, manda_params[0])            
+        elif params and not all_params:
+            raise CmdUnknownParamSyntaxError(cmd.module, cmd.command, 
+                                             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
+                # the 'name' must be an integer (ie. the position of
+                # an unnamed argument
+                if type(name) == int:
+                    # lump all extraneous arguments together as one big final one
+                    # todo: check if last param type is a string?
+                    if (param_count > 2):
+                        while (param_count > len(command_info.params) - 1):
+                            params[param_count - 2] += params[param_count - 1]
+                            del(params[param_count - 1])
+                            param_count = len(params)
+                            cmd.params = params.copy()
+
+                    # (-1, help is always in the all_params list)
+                    if name >= len(all_params) - 1:
+                        # add to last known param
+                        if param_name:
+                            cmd.params[param_name] += cmd.params[name]
+                        else:
+                            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)
+                        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):
+        #to do, consist xml package and send to bind10
+        if cmd.command == "help" or ("help" in cmd.params.keys()):
+            self._handle_help(cmd)
+        elif cmd.module == "config":
+            self.apply_config_cmd(cmd)
+        else:
+            self.apply_cmd(cmd)
+
+    def add_module_info(self, module_info):        
+        self.modules[module_info.name] = module_info
+        
+    def get_module_names(self):
+        return list(self.modules.keys())
+
+    #override methods in cmd
+    def default(self, line):
+        self._parse_cmd(line)
+
+    def emptyline(self):
+        pass
+
+    def do_help(self, name):
+        print(CONST_BINDCTL_HELP)
+        for k in self.modules.keys():
+            print("\t", self.modules[k])
+                
+    
+    def onecmd(self, line):
+        if line == 'EOF' or line.lower() == "quit":
+            self.conn.close()
+            return True
+            
+        if line == 'h':
+            line = 'help'
+        
+        Cmd.onecmd(self, line)
+                    
+    def complete(self, text, state):
+        if 0 == state:
+            text = text.strip()
+            hints = []
+            cur_line = readline.get_line_buffer()            
+            try:
+                cmd = BindCmdParse(cur_line)
+                if not cmd.params and text:
+                    hints = self._get_command_startswith(cmd.module, text)
+                else:                       
+                    hints = self._get_param_startswith(cmd.module, cmd.command,
+                                                       text)
+                    if cmd.module == "config":
+                        # grm text has been stripped of slashes...
+                        my_text = self.location + "/" + cur_line.rpartition(" ")[2]
+                        list = self.config_data.config.get_item_list(my_text.rpartition("/")[0])
+                        hints.extend([val for val in list if val.startswith(text)])
+            except CmdModuleNameFormatError:
+                if not text:
+                    hints = self.get_module_names()
+
+            except CmdMissCommandNameFormatError as e:
+                if not text.strip(): # command name is empty
+                    hints = self.modules[e.module].get_command_names()                    
+                else: 
+                    hints = self._get_module_startswith(text)
+
+            except CmdCommandNameFormatError as e:
+                if e.module in self.modules:
+                    hints = self._get_command_startswith(e.module, text)
+
+            except CmdParamFormatError as e:
+                hints = self._get_param_startswith(e.module, e.command, text)
+
+            except BindCtlException:
+                hints = []
+
+            self.hint = hints
+            #self._append_space_to_hint()
+
+        if state < len(self.hint):
+            return self.hint[state]
+        else:
+            return None
+            
+
+    def _get_module_startswith(self, text):       
+        return [module
+                for module in self.modules 
+                if module.startswith(text)]
+
+
+    def _get_command_startswith(self, module, text):
+        if module in self.modules:            
+            return [command
+                    for command in self.modules[module].get_command_names() 
+                    if command.startswith(text)]
+        
+        return []                    
+                        
+
+    def _get_param_startswith(self, module, command, text):        
+        if module in self.modules:
+            module_info = self.modules[module]            
+            if command in module_info.get_command_names():                
+                cmd_info = module_info.get_command_with_name(command)
+                params = cmd_info.get_param_names() 
+                hint = []
+                if text:    
+                    hint = [val for val in params if val.startswith(text)]
+                else:
+                    hint = list(params)
+                
+                if len(hint) == 1 and hint[0] != "help":
+                    hint[0] = hint[0] + " ="    
+                
+                return hint
+
+        return []
+
+    def _parse_cmd(self, line):
+        try:
+            cmd = BindCmdParse(line)
+            self.validate_cmd(cmd)
+            self._handle_cmd(cmd)
+        except BindCtlException as e:
+            print("Error! ", e)
+            self._print_correct_usage(e)
+            
+            
+    def _print_correct_usage(self, ept):        
+        if isinstance(ept, CmdUnknownModuleSyntaxError):
+            self.do_help(None)
+            
+        elif isinstance(ept, CmdUnknownCmdSyntaxError):
+            self.modules[ept.module].module_help()
+            
+        elif isinstance(ept, CmdMissParamSyntaxError) or \
+             isinstance(ept, CmdUnknownParamSyntaxError):
+             self.modules[ept.module].command_help(ept.command)
+                 
+                
+    def _append_space_to_hint(self):
+        """Append one space at the end of complete hint."""
+        self.hint = [(val + " ") for val in self.hint]
+            
+            
+    def _handle_help(self, cmd):
+        if cmd.command == "help":
+            self.modules[cmd.module].module_help()
+        else:
+            self.modules[cmd.module].command_help(cmd.command)
+
+
+    def apply_config_cmd(self, cmd):
+        identifier = self.location
+        try:
+            if 'identifier' in cmd.params:
+                if not identifier.endswith("/"):
+                    identifier += "/"
+                if cmd.params['identifier'].startswith("/"):
+                    identifier = cmd.params['identifier']
+                else:
+                    identifier += cmd.params['identifier']
+            if cmd.command == "show":
+                values = self.config_data.get_value_maps(identifier)
+                for value_map in values:
+                    line = value_map['name']
+                    if value_map['type'] in [ 'module', 'map', 'list' ]:
+                        line += "/"
+                    else:
+                        line += ":\t" + str(value_map['value'])
+                    line += "\t" + value_map['type']
+                    line += "\t"
+                    if value_map['default']:
+                        line += "(default)"
+                    if value_map['modified']:
+                        line += "(modified)"
+                    print(line)
+            elif cmd.command == "add":
+                self.config_data.add(identifier, cmd.params['value'])
+            elif cmd.command == "remove":
+                self.config_data.remove(identifier, cmd.params['value'])
+            elif cmd.command == "set":
+                self.config_data.set(identifier, cmd.params['value'])
+            elif cmd.command == "unset":
+                self.config_data.unset(identifier)
+            elif cmd.command == "revert":
+                self.config_data.revert()
+            elif cmd.command == "commit":
+                self.config_data.commit(self)
+            elif cmd.command == "go":
+                self.go(identifier)
+        except ISC.CC.data.DataTypeError as dte:
+            print("Error: " + str(dte))
+        except ISC.CC.data.DataNotFoundError as dnfe:
+            print("Error: " + identifier + " not found")
+        except KeyError as ke:
+            print("Error: missing " + str(ke))
+            raise ke
+
+    def go(self, identifier):
+        # just to see if it exists
+        self.config_data.get_value(identifier)
+        # some sanitizing
+        identifier = identifier.replace("//", "/")
+        if not identifier.startswith("/"):
+            identifier = "/" + identifier
+        if identifier.endswith("/"):
+            identifier = identifier[:-1]
+        self.location = identifier
+
+    def apply_cmd(self, cmd):
+        url = '/' + cmd.module + '/' + cmd.command
+        cmd_params = None
+        if (len(cmd.params) != 0):
+            cmd_params = json.dumps(cmd.params)
+
+        print("send the message to cmd-ctrld")        
+        reply = self.send_POST(url, cmd.params)
+        data = reply.read().decode()
+        print("received reply:", data)
+
+
+
+

+ 1 - 1
src/bin/bindctl/bindctl.in

@@ -5,7 +5,7 @@ export PYTHON_EXEC
 
 BINDCTL_PATH=@abs_top_srcdir@/src/bin/bindctl
 
-PYTHONPATH=@abs_top_srcdir@/src/lib/cc/python:@abs_top_srcdir@/src/lib/bindctl/
+PYTHONPATH=@abs_top_srcdir@/src/lib/cc/python
 export PYTHONPATH
 
 cd ${BINDCTL_PATH}

src/bin/bindctl/create_your_cert.pem → src/bin/bindctl/bindctl.pem


+ 121 - 0
src/bin/bindctl/cmdparse.py

@@ -0,0 +1,121 @@
+# Copyright (C) 2009  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+import re
+from exception import *
+try:
+    from collections import OrderedDict
+except ImportError:
+    from mycollections import OrderedDict
+
+param_name_str = "^\s*(?P<param_name>[\w]+)\s*=\s*"
+param_value_str = "(?P<param_value>[\w\./-]+)"
+param_value_with_quota_str = "[\"\'](?P<param_value>[\w\., /-]+)[\"\']"
+next_params_str = "(?P<blank>\s*)(?P<comma>,?)(?P<next_params>.*)$"
+
+PARAM_WITH_QUOTA_PATTERN = re.compile(param_name_str + 
+                                      param_value_with_quota_str +
+                                      next_params_str)
+PARAM_PATTERN = re.compile(param_name_str + param_value_str + next_params_str)
+                           
+# Used for module and command name
+NAME_PATTERN = re.compile("^\s*(?P<name>[\w]+)(?P<blank>\s*)(?P<others>.*)$")
+
+class BindCmdParse:
+    """ 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, 
+    parameter part is optional
+    
+    Example: zone reload, zone_name=example.com 
+    module == zone
+    command == reload
+    params == [zone_name = 'example.com']
+    """
+    
+    def __init__(self, cmd):
+        self.params = OrderedDict()
+        self.module = ''
+        self.command = ''
+        self._parse_cmd(cmd)
+
+    def _parse_cmd(self, text_str):    
+        # Get module name
+        groups = NAME_PATTERN.match(text_str)
+        if not groups:
+            raise CmdModuleNameFormatError
+        
+        self.module = groups.group('name')
+        cmd_str = groups.group('others')
+        if cmd_str:
+            if not groups.group('blank'):
+                raise CmdModuleNameFormatError
+        else:            
+            raise CmdMissCommandNameFormatError(self.module)
+            
+        # Get command name
+        groups = NAME_PATTERN.match(cmd_str)
+        if (not groups):
+            raise CmdCommandNameFormatError(self.module)
+        
+        self.command = groups.group('name')
+        param_str = groups.group('others')
+        if param_str:
+            if not groups.group('blank'):
+                raise CmdCommandNameFormatError(self.module)
+
+            self._parse_params(param_str)
+
+
+    def _parse_params(self, param_text):
+        """convert a=b,c=d into one hash """
+        
+        # Check parameter name "help"
+        param = NAME_PATTERN.match(param_text)
+        if param and param.group('name') == "help":
+            self.params["help"] = "help"
+            return
+
+        while True:
+            if not param_text.strip():
+                break
+                
+            groups = PARAM_PATTERN.match(param_text) or \
+                     PARAM_WITH_QUOTA_PATTERN.match(param_text)
+            
+            if not groups:
+                # ok, fill in the params in the order entered
+                params = re.findall("([^\" ]+|\".*\")", param_text)
+                i = 0
+                for p in params:
+                    self.params[i] = p
+                    i += 1
+                break
+            else:
+                self.params[groups.group('param_name')] = groups.group('param_value')
+                param_text = groups.group('next_params')
+                if not param_text or (not param_text.strip()):
+                    break
+
+                if not groups.group('blank') and \
+                   not groups.group('comma'):
+                    raise CmdParamFormatError(self.module, self.command)
+                    
+                
+            
+            
+            
+    
+

+ 119 - 0
src/bin/bindctl/exception.py

@@ -0,0 +1,119 @@
+# Copyright (C) 2009  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+class BindCtlException(Exception):
+    """Abstract base class shared by all bindctl exceptions"""
+    def __str__(self):
+        return "Big tool has problem"
+
+# Begin define Format exception
+
+class CmdFormatError(BindCtlException):
+    """Command is malformed"""
+    def __str__(self):
+        return "Command is malformed"
+
+        
+class CmdModuleNameFormatError(CmdFormatError):
+    """module name format error"""
+
+    def __str__(self):
+        return "Module name format error: the charater of name can only be '0-9a-zA-Z_'" 
+                      
+                
+class CmdCommandNameFormatError(CmdFormatError):
+    """command name format error"""
+    
+    def __init__(self, module):
+        self.module = module        
+        
+    def __str__(self):
+        return "Command name format error: the charater of name can only be '0-9a-zA-Z_'"      
+        
+        
+class CmdMissCommandNameFormatError(CmdFormatError):
+    """Module name isn't finished"""
+    
+    def __init__(self, module):
+        self.module = module
+        
+    def __str__(self):
+        return "command name is missed"   
+
+
+class CmdParamFormatError(CmdFormatError):
+    """Command is malformed which parameter isn't key value pair"""
+    
+    def __init__(self, module, command):        
+        self.module = module
+        self.command = command        
+
+    def __str__(self):
+        return  "Parameter format error, it should like 'key = value'"         
+        
+# Begin define the exception for syntax
+
+class CmdSyntaxError(BindCtlException):
+    """Command line has syntax error"""
+    
+    def __str__(self):
+        return "Command line has syntax error"
+
+
+class CmdUnknownModuleSyntaxError(CmdSyntaxError):
+    """Command is unknown"""
+    def __init__(self, module):
+        self.module = module
+
+    def __str__(self):
+        return str("Unknown module '%s'" % self.module)
+        
+
+class CmdUnknownCmdSyntaxError(CmdSyntaxError):
+    """Command is unknown"""
+    def __init__(self, module, command):
+        self.module = module
+        self.command = command
+
+    def __str__(self):
+        return str("Unknown command '%s' to module '%s'" % 
+                    (self.command, self.module))
+                    
+
+class CmdUnknownParamSyntaxError(CmdSyntaxError):
+    """The parameter of command is unknown"""
+    def __init__(self, module, command, param):
+        self.module = module
+        self.command = command
+        self.param = param
+
+    def __str__(self):
+        return str("Unknown parameter '%s' to command '%s' of module '%s'" %
+                   (self.param, self.command, self.module))
+                   
+
+class CmdMissParamSyntaxError(CmdSyntaxError):
+    """The parameter of one command is missed"""
+    def __init__(self, module, command, param):
+        self.module = module
+        self.command = command
+        self.param = param
+
+    def __str__(self):
+        return str("Parameter '%s' is missed for command '%s' of module '%s'" % 
+                   (self.param, self.command, self.module))
+                   
+   

+ 230 - 0
src/bin/bindctl/moduleinfo.py

@@ -0,0 +1,230 @@
+# Copyright (C) 2009  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+
+try:
+    from collections import OrderedDict
+except ImportError:
+    from mycollections import OrderedDict
+
+# Define value type
+STRING_TYPE = "string"
+LIST_TYPE = "list"
+INT_TYPE = "int"
+
+MODULE_NODE_NAME = 'module'
+COMMAND_NODE_NAME = 'command'
+PARAM_NODE_NAME = 'param'
+
+
+class ParamInfo:
+    """The parameter of one command
+    each command parameter have four attributes, 
+    parameter name, parameter type, parameter value, and parameter description
+    """
+    def __init__(self, name, desc = '', type = STRING_TYPE, 
+                 optional = False, value = '', default_value = ''):
+        self.name = name
+        self.type = type
+        self.value = value
+        self.default_value = default_value                           
+        self.desc = desc
+        self.is_optional = optional
+    
+    def __str__(self):        
+        return str("\t%s <type: %s> \t(%s)" % (self.name, self.type, self.desc))
+
+class CommandInfo:
+    """One command which provide by one bind10 module, it has zero or 
+    more parameters
+    """
+
+    def __init__(self, name, desc = "", need_inst_param = True):
+        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"
+        self.add_param(ParamInfo("help", 
+                                  desc = "Get help for command",
+                                  optional = True))
+                
+    def __str__(self):
+        return str("%s \t(%s)" % (self.name, self.desc))
+        
+
+    def add_param(self, paraminfo):
+        self.params[paraminfo.name] = paraminfo
+        
+
+    def has_param_with_name(self, param_name):
+        return param_name in self.params
+        
+
+    def get_param_with_name(self, param_name):
+        return self.params[param_name]
+        
+
+    def get_params(self):
+        return list(self.params.values())
+        
+
+    def get_param_names(self):
+        return list(self.params.keys())
+        
+        
+    def get_mandatory_param_names(self):
+        all_names = self.params.keys()
+        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):
+        # 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
+        # if the number of lefts is higher than the number of mandatories,
+        # use the first optional. Otherwise, use the first unhandled mandatory
+        # (and update the location accordingly?)
+        # (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 param_count == len(self.params) - 1:
+                i = 0
+                for k in self.params.keys():
+                    if i == pos:
+                        return k
+                    i += 1
+                raise KeyError(str(pos) + " out of range")
+            elif param_count <= len(self.params):
+                mandatory_count = 0
+                for k in self.params.keys():
+                    if not self.params[k].is_optional:
+                        mandatory_count += 1
+                if param_count == mandatory_count:
+                    # return the first mandatory from pos
+                    i = 0
+                    for k in self.params.keys():
+                        if i >= pos and not self.params[k].is_optional:
+                            return k
+                        i += 1
+                    raise KeyError(str(pos) + " out of range")
+                else:
+                    i = 0
+                    for k in self.params.keys():
+                        if i == pos:
+                            return k
+                        i += 1
+                    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):
+        print("Command ", self)
+        print("\t\thelp (Get help for command)")
+                
+        params = self.params.copy()
+        del params["help"]
+
+        if len(params) == 0:
+            print("\tNo parameters for the command")
+            return
+        
+        print("\n\tMandatory parameters:")
+        mandatory_infos = []
+        for info in params.values():            
+            if not info.is_optional:
+                print("\t", info)
+                mandatory_infos.append(info)
+
+        optional_infos = [info for info in params.values() 
+                          if info not in mandatory_infos]
+        if len(optional_infos) > 0:
+            print("\n\tOptional parameters:")      
+            for info in optional_infos:
+                    print("\t", info)
+
+
+class ModuleInfo:
+    """Define the information of one module, include module name, 
+    module supporting commands, instance name and the value type of instance name
+    """    
+    
+    def __init__(self, name, inst_name = "", inst_type = STRING_TYPE, 
+                 inst_desc = "", 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))
+        
+    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)
+    
+

+ 67 - 0
src/bin/bindctl/mycollections.py

@@ -0,0 +1,67 @@
+from collections import MutableMapping
+
+class OrderedDict(dict, MutableMapping):
+
+    # Methods with direct access to underlying attributes
+
+    def __init__(self, *args, **kwds):
+        if len(args) > 1:
+            raise TypeError('expected at 1 argument, got %d', len(args))
+        if not hasattr(self, '_keys'):
+            self._keys = []
+        self.update(*args, **kwds)
+
+    def clear(self):
+        del self._keys[:]
+        dict.clear(self)
+
+    def __setitem__(self, key, value):
+        if key not in self:
+            self._keys.append(key)
+        dict.__setitem__(self, key, value)
+
+    def __delitem__(self, key):
+        dict.__delitem__(self, key)
+        self._keys.remove(key)
+
+    def __iter__(self):
+        return iter(self._keys)
+
+    def __reversed__(self):
+        return reversed(self._keys)
+
+    def popitem(self):
+        if not self:
+            raise KeyError
+        key = self._keys.pop()
+        value = dict.pop(self, key)
+        return key, value
+
+    def __reduce__(self):
+        items = [[k, self[k]] for k in self]
+        inst_dict = vars(self).copy()
+        inst_dict.pop('_keys', None)
+        return (self.__class__, (items,), inst_dict)
+
+    # Methods with indirect access via the above methods
+
+    setdefault = MutableMapping.setdefault
+    update = MutableMapping.update
+    pop = MutableMapping.pop
+    keys = MutableMapping.keys
+    values = MutableMapping.values
+    items = MutableMapping.items
+
+    def __repr__(self):
+        pairs = ', '.join(map('%r: %r'.__mod__, self.items()))
+        return '%s({%s})' % (self.__class__.__name__, pairs)
+
+    def copy(self):
+        return self.__class__(self)
+
+    @classmethod
+    def fromkeys(cls, iterable, value=None):
+        d = cls()
+        for key in iterable:
+            d[key] = value
+        return d

src/bin/cmdctl/create_your_cert.pem → src/bin/cmdctl/b10-cmdctl.pem


+ 2 - 2
src/bin/cmdctl/b10-cmdctl.py

@@ -202,8 +202,8 @@ class SecureHTTPServer(http.server.HTTPServer):
         try:
             connstream = ssl.wrap_socket(newsocket,
                                      server_side = True,
-                                     certfile = 'create_your_cert.pem',
-                                     keyfile = 'create_your_cert.pem',
+                                     certfile = 'b10-cmdctl.pem',
+                                     keyfile = 'b10-cmdctl.pem',
                                      ssl_version = ssl.PROTOCOL_SSLv23)
             return (connstream, fromaddr)
         except ssl.SSLError as e :