From 2019e5737cadd09101ebfdc70698686e56ee372f Mon Sep 17 00:00:00 2001 From: Likun Zhang Date: Thu, 21 Jan 2010 09:08:32 +0000 Subject: [PATCH] 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 --- configure.ac | 3 + src/lib/bind-cfgd/python/bind-cfgd.py | 32 ++--- src/lib/bindctl/bindctl.py | 181 ++++++++++++++++---------- src/lib/bindctl/command.py | 4 +- src/lib/cc/python/ISC/CC/data.py | 69 +++------- 5 files changed, 147 insertions(+), 142 deletions(-) diff --git a/configure.ac b/configure.ac index c9bfdc72d5..a875a09c65 100644 --- a/configure.ac +++ b/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 diff --git a/src/lib/bind-cfgd/python/bind-cfgd.py b/src/lib/bind-cfgd/python/bind-cfgd.py index 7df49112b7..5f40d47f38 100644 --- a/src/lib/bind-cfgd/python/bind-cfgd.py +++ b/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) ] diff --git a/src/lib/bindctl/bindctl.py b/src/lib/bindctl/bindctl.py index 77d10bf48e..f49da5c33e 100644 --- a/src/lib/bindctl/bindctl.py +++ b/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]) + url = '/' + cmd.module + '/' + cmd.command + cmd_params = None + if (len(cmd.params) != 0): + cmd_params = json.dumps(cmd.params) - 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) + print("send the message to cmd-ctrld") + reply = self.send_POST(url, cmd.params) + data = reply.read().decode() + print("received reply:", data) diff --git a/src/lib/bindctl/command.py b/src/lib/bindctl/command.py index 43c4099713..e967daa421 100644 --- a/src/lib/bindctl/command.py +++ b/src/lib/bindctl/command.py @@ -6,8 +6,8 @@ except ImportError: from mycollections import OrderedDict param_name_str = "^\s*(?P[\w]+)\s*=\s*" -param_value_str = "(?P[\w\./]+)" -param_value_with_quota_str = "[\"\'](?P[\w\., /]+)[\"\']" +param_value_str = "(?P[\w\./-]+)" +param_value_with_quota_str = "[\"\'](?P[\w\., /-]+)[\"\']" next_params_str = "(?P\s*)(?P,?)(?P.*)$" PARAM_WITH_QUOTA_PATTERN = re.compile(param_name_str + diff --git a/src/lib/cc/python/ISC/CC/data.py b/src/lib/cc/python/ISC/CC/data.py index d3afc6f200..475832c020 100644 --- a/src/lib/cc/python/ISC/CC/data.py +++ b/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)