Browse Source

Change the code to support new module 'cmd-ctrld'.
When bindctl starts up, it needs login into cmd-ctrld. The communication between bindctl and cmd-ctrld is protected by SSL.


git-svn-id: svn://bind10.isc.org/svn/bind10/branches/parkinglot@494 e5f2f494-b856-4b98-b285-d166d9295462

Likun Zhang 15 years ago
parent
commit
2019e5737c

+ 3 - 0
configure.ac

@@ -114,6 +114,7 @@ AC_CONFIG_FILES([Makefile
                  src/Makefile
                  src/bin/Makefile
                  src/bin/bind10/Makefile
+				 src/bin/cmd-ctrld/Makefile
                  src/bin/bindctl/Makefile
                  src/bin/host/Makefile
                  src/bin/msgq/Makefile
@@ -124,6 +125,7 @@ AC_CONFIG_FILES([Makefile
                  src/lib/dns/Makefile
                ])
 AC_OUTPUT([src/bin/bind-cfgd/bind-cfgd
+           src/bin/cmd-ctrld/cmd-ctrld
            src/bin/bind10/bind10
            src/bin/bind10/bind10_test
            src/bin/bindctl/run_bindctl
@@ -132,6 +134,7 @@ AC_OUTPUT([src/bin/bind-cfgd/bind-cfgd
            src/bin/parkinglot/config.h
           ], [
            chmod +x src/bin/bind-cfgd/bind-cfgd
+           chmod +x src/bin/cmd-ctrld/cmd-ctrld
            chmod +x src/bin/bind10/bind10
            chmod +x src/bin/bindctl/run_bindctl
            chmod +x src/bin/msgq/msgq

+ 12 - 20
src/lib/bind-cfgd/python/bind-cfgd.py

@@ -93,11 +93,12 @@ class ConfigManager:
                 if cmd[0] == "get_commands":
                     answer["result"] = [ 0, self.commands ]
                 elif cmd[0] == "get_data_spec":
-                    if len(cmd) > 1 and cmd[1] != "":
+                    if len(cmd) > 1 and cmd[1]['module_name'] != '':
+                        module_name = cmd[1]['module_name']
                         try:
-                            answer["result"] = [0, self.data_definitions[cmd[1]]]
+                            answer["result"] = [0, self.data_definitions[module_name]]
                         except KeyError as ke:
-                            answer["result"] = [1, "No specification for module " + cmd[1]]
+                            answer["result"] = [1, "No specification for module " + module_name]
                     else:
                         answer["result"] = [0, self.data_definitions]
                 elif cmd[0] == "get_config":
@@ -105,7 +106,7 @@ class ConfigManager:
                     conf_part = None
                     if len(cmd) > 1:
                         try:
-                            conf_part = data.find(self.config.data, cmd[1])
+                            conf_part = data.find(self.config.data, cmd[1]['module_name'])
                         except data.DataNotFoundError as dnfe:
                             pass
                     else:
@@ -114,6 +115,7 @@ class ConfigManager:
                         answer["result"] = [ 0, conf_part ]
                     else:
                         answer["result"] = [ 0 ]
+
                 elif cmd[0] == "set_config":
                     if len(cmd) == 3:
                         # todo: use api (and check types?)
@@ -143,20 +145,8 @@ class ConfigManager:
                         answer["result"] = [ 0 ]
                     else:
                         answer["result"] = [ 1, "Wrong number of arguments" ]
-                elif cmd[0] == "zone" and cmd[1] == "add":
-                    self.add_zone(cmd[2])
-                    answer["result"] = [ 0 ]
-                elif cmd[0] == "zone" and cmd[1] == "remove":
-                    try:
-                        self.remove_zone(cmd[2])
-                        answer["result"] = [ 0 ]
-                    except KeyError:
-                        # zone wasn't there, should we make
-                        # a separate exception for that?
-                        answer["result"] = [ 1, "Unknown zone" ]
-                elif cmd[0] == "zone" and cmd[1] == "list":
-                    answer["result"]     = []#list(self.config.zones.keys())
-                elif cmd == "shutdown":
+
+                elif cmd[0] == "shutdown":
                     print("[bind-cfgd] Received shutdown command")
                     self.running = False
                 else:
@@ -171,11 +161,13 @@ class ConfigManager:
             spec = msg["data_specification"]
             if "config_data" in spec:
                 self.set_config(spec["module_name"], spec["config_data"])
-                self.cc.group_sendmsg({ "specification_update": [ spec["module_name"], spec["config_data"] ] }, "BindCtl")
+                self.cc.group_sendmsg({ "specification_update": [ spec["module_name"], spec["config_data"] ] }, "Cmd-Ctrld")
             if "commands" in spec:
                 self.set_commands(spec["module_name"], spec["commands"])
-                self.cc.group_sendmsg({ "commands_update": [ spec["module_name"], spec["commands"] ] }, "BindCtl")
+                self.cc.group_sendmsg({ "commands_update": [ spec["module_name"], spec["commands"] ] }, "Cmd-Ctrld")
             answer["result"] = [ 0 ]
+        elif 'result' in msg:
+            answer['result'] = [0]
         else:
             print("[bind-cfgd] unknown message: " + str(msg))
             answer["result"] = [ 1, "Unknown module: " + str(msg) ]

+ 116 - 67
src/lib/bindctl/bindctl.py

@@ -7,6 +7,13 @@ from command import BindCtlCmd
 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
+from hashlib import sha1
 
 try:
     from collections import OrderedDict
@@ -27,7 +34,7 @@ CONST_COMMAND_NODE = "command"
 class BindCtl(Cmd):
     """simple bindctl example."""    
 
-    def __init__(self, session = None):
+    def __init__(self, server_port = 'localhost:8080'):
         Cmd.__init__(self)
         self.location = ""
         self.prompt_end = '> '
@@ -35,13 +42,105 @@ class BindCtl(Cmd):
         self.ruler = '-'
         self.modules = OrderedDict()
         self.add_module_info(ModuleInfo("help", desc = "Get help for bindctl"))
-        self.cc = session
-        self.config_data = ISC.CC.data.UIConfigData("", session)
+        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='create_your_cert.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):
+        count = 0
+        print("[TEMP MESSAGE]: username :root  password :bind10")
+        try:
+            while count < 3:
+                count = count + 1
+                username = input("username:")
+                passwd = input("password:")
+                param = {'username': username, 'password' : passwd}
+                response = self.send_POST('/', param)
+                data = response.read().decode()
+                print(data)
+            
+                if response.status == http.client.OK:
+                    break
+                if count == 3:
+                    print("Too many authentication failures")
+                    return True
+
+            # 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 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)
@@ -127,13 +226,7 @@ class BindCtl(Cmd):
 
     def emptyline(self):
         pass
-    
-    def cmdloop(self):
-        try:
-            Cmd.cmdloop(self)
-        except KeyboardInterrupt:
-            return True
-            
+
     def do_help(self, name):
         print(CONST_BINDCTL_HELP)
         for k in self.modules.keys():
@@ -141,9 +234,8 @@ class BindCtl(Cmd):
                 
     
     def onecmd(self, line):
-        # check if there's anything on the cc first
-        self.check_cc_messages()
         if line == 'EOF' or line.lower() == "quit":
+            self.conn.close()
             return True
             
         if line == 'h':
@@ -170,8 +262,8 @@ class BindCtl(Cmd):
                         hints.extend([val for val in list if val.startswith(text)])
             except CmdModuleNameFormatError:
                 if not text:
-                    hints = list(self.modules.keys())
-                    
+                    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()                    
@@ -230,36 +322,8 @@ class BindCtl(Cmd):
                 return hint
 
         return []
-        
-    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 check_cc_messages(self):
-        (message, env) = self.cc.group_recvmsg(True)
-        while message:
-            if 'commands_update' in message:
-                self.prepare_module_commands(message['commands_update'][0], message['commands_update'][1])
-            elif 'specification_update' in message:
-                self.config_data.config.specification[message['specification_update'][0]] = message['specification_update'][1]
-            (message, env) = self.cc.group_recvmsg(True)
 
     def _parse_cmd(self, line):
-        # check if there's anything on the cc first
-        self.check_cc_messages()
         try:
             cmd = BindCtlCmd(line)
             self.validate_cmd(cmd)
@@ -283,7 +347,6 @@ class BindCtl(Cmd):
                 
     def _append_space_to_hint(self):
         """Append one space at the end of complete hint."""
-        
         self.hint = [(val + " ") for val in self.hint]
             
             
@@ -330,7 +393,7 @@ class BindCtl(Cmd):
             elif cmd.command == "revert":
                 self.config_data.revert()
             elif cmd.command == "commit":
-                self.config_data.commit(self.cc)
+                self.config_data.commit(self)
             elif cmd.command == "go":
                 self.go(identifier)
         except ISC.CC.data.DataTypeError as dte:
@@ -353,29 +416,15 @@ class BindCtl(Cmd):
         self.location = identifier
 
     def apply_cmd(self, cmd):
-        if not self.cc:
-            return
-        
-        groupName = cmd.module
-        content = [cmd.module, cmd.command]
-        values = cmd.params.values()
-        if len(values) > 0:
-            content.append(list(values)[0])
-
-        msg = {"command":content}
-        print("begin to send the message...")
-
-        # XXTODO: remove this with new msgq
-        #self.cc.group_subscribe(groupName)
-        
-        try:   
-            self.cc.group_sendmsg(msg, groupName)
-            print("waiting for %s reply..." % groupName)
-
-            reply, env = self.cc.group_recvmsg(False)
-            print("received reply:", reply)
-        except:
-            print("Error communication with %s" % groupName)
+        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)
 
 
 

+ 2 - 2
src/lib/bindctl/command.py

@@ -6,8 +6,8 @@ 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\., /]+)[\"\']"
+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 + 

+ 15 - 54
src/lib/cc/python/ISC/CC/data.py

@@ -17,7 +17,7 @@ def merge(orig, new):
                 else:
                     orig[kn] = new[kn]
             else:
-                orig.remove(kn)
+                del orig[kn]
         else:
             orig[kn] = new[kn]
 
@@ -180,63 +180,24 @@ class ConfigData:
         return None, False
 
 class UIConfigData():
-    def __init__(self, name, cc):
+    def __init__(self, conn, name = ''):
         self.module_name = name
-        data_spec = self.get_data_specification(cc)
+        data_spec = self.get_data_specification(conn)
         self.config = ConfigData(data_spec)
-        self.get_config_data(cc)
+        self.get_config_data(conn)
         self.config_changes = {}
     
-    def get_config_data(self, cc):
-        cc.group_sendmsg({ "command": ["get_config", self.module_name] }, "ConfigManager")
-        answer, env = cc.group_recvmsg(False)
-        if 'result' in answer.keys() and type(answer['result']) == list:
-            # TODO: with the new cc implementation, replace "1" by 1
-            if answer['result'][0] == "1":
-                # todo: exception
-                print("Error: " + str(answer['result'][1]))
-            else:
-                self.config.data = answer['result'][1]
-        else:
-            # XX todo: raise exc
-            print("Error: unexpected answer from config manager:")
-            print(answer)
+    def get_config_data(self, conn):
+        self.config.data = conn.send_GET('/config_data') 
 
-    def send_changes(self, cc):
-        """Sends the changes configuration values to the config manager.
-           If the command succeeds, the changes are re-requested and
-           the changed list is reset"""
-        if self.module_name and self.module_name != "":
-            cc.group_sendmsg({ "command": [ "set_config", self.module_name, self.config_changes ]}, "ConfigManager")
-        else:
-            cc.group_sendmsg({ "command": [ "set_config", self.config_changes ]}, "ConfigManager")
-        answer, env = cc.group_recvmsg(False)
-        if 'result' in answer and type(answer['result']) == list:
-            if answer['result'][0] == 0:
-                # ok
-                self.get_config_data(cc)
-                self.config_changes = {}
-            else:
-                print("Error committing changes: " + answer['result'][1])
-        else:
-            print("Error: unexpected answer: " + str(answer))
+    def send_changes(self, conn):
+        conn.send_POST('/ConfigManager/set_config', self.config_changes)
+        # Get latest config data
+        self.get_config_data(conn)
+        self.config_changes = {}
     
-    def get_data_specification(self, cc):
-        cc.group_sendmsg({ "command": ["get_data_spec", self.module_name] }, "ConfigManager")
-        answer, env = cc.group_recvmsg(False)
-        if 'result' in answer.keys() and type(answer['result']) == list:
-            # TODO: with the new cc implementation, replace "1" by 1
-            if answer['result'][0] == "1":
-                # todo: exception
-                print("Error: " + str(answer['result'][1]))
-                return None
-            else:
-                return answer['result'][1]
-        else:
-            # XX todo: raise exc
-            print("Error: unexpected answer from config manager:")
-            print(answer)
-        return None
+    def get_data_specification(self, conn):
+        return conn.send_GET('/config_spec') 
 
     def set(self, identifier, value):
         # check against definition
@@ -371,5 +332,5 @@ class UIConfigData():
     def revert(self):
         self.config_changes = {}
 
-    def commit(self, cc):
-        self.send_changes(cc)
+    def commit(self, conn):
+        self.send_changes(conn)