Commit 2019e573 authored by Likun Zhang's avatar Likun Zhang
Browse files

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
parent e3e8cda0
...@@ -114,6 +114,7 @@ AC_CONFIG_FILES([Makefile ...@@ -114,6 +114,7 @@ AC_CONFIG_FILES([Makefile
src/Makefile src/Makefile
src/bin/Makefile src/bin/Makefile
src/bin/bind10/Makefile src/bin/bind10/Makefile
src/bin/cmd-ctrld/Makefile
src/bin/bindctl/Makefile src/bin/bindctl/Makefile
src/bin/host/Makefile src/bin/host/Makefile
src/bin/msgq/Makefile src/bin/msgq/Makefile
...@@ -124,6 +125,7 @@ AC_CONFIG_FILES([Makefile ...@@ -124,6 +125,7 @@ AC_CONFIG_FILES([Makefile
src/lib/dns/Makefile src/lib/dns/Makefile
]) ])
AC_OUTPUT([src/bin/bind-cfgd/bind-cfgd AC_OUTPUT([src/bin/bind-cfgd/bind-cfgd
src/bin/cmd-ctrld/cmd-ctrld
src/bin/bind10/bind10 src/bin/bind10/bind10
src/bin/bind10/bind10_test src/bin/bind10/bind10_test
src/bin/bindctl/run_bindctl src/bin/bindctl/run_bindctl
...@@ -132,6 +134,7 @@ AC_OUTPUT([src/bin/bind-cfgd/bind-cfgd ...@@ -132,6 +134,7 @@ AC_OUTPUT([src/bin/bind-cfgd/bind-cfgd
src/bin/parkinglot/config.h src/bin/parkinglot/config.h
], [ ], [
chmod +x src/bin/bind-cfgd/bind-cfgd 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/bind10/bind10
chmod +x src/bin/bindctl/run_bindctl chmod +x src/bin/bindctl/run_bindctl
chmod +x src/bin/msgq/msgq chmod +x src/bin/msgq/msgq
......
...@@ -93,11 +93,12 @@ class ConfigManager: ...@@ -93,11 +93,12 @@ class ConfigManager:
if cmd[0] == "get_commands": if cmd[0] == "get_commands":
answer["result"] = [ 0, self.commands ] answer["result"] = [ 0, self.commands ]
elif cmd[0] == "get_data_spec": 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: try:
answer["result"] = [0, self.data_definitions[cmd[1]]] answer["result"] = [0, self.data_definitions[module_name]]
except KeyError as ke: except KeyError as ke:
answer["result"] = [1, "No specification for module " + cmd[1]] answer["result"] = [1, "No specification for module " + module_name]
else: else:
answer["result"] = [0, self.data_definitions] answer["result"] = [0, self.data_definitions]
elif cmd[0] == "get_config": elif cmd[0] == "get_config":
...@@ -105,7 +106,7 @@ class ConfigManager: ...@@ -105,7 +106,7 @@ class ConfigManager:
conf_part = None conf_part = None
if len(cmd) > 1: if len(cmd) > 1:
try: 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: except data.DataNotFoundError as dnfe:
pass pass
else: else:
...@@ -114,6 +115,7 @@ class ConfigManager: ...@@ -114,6 +115,7 @@ class ConfigManager:
answer["result"] = [ 0, conf_part ] answer["result"] = [ 0, conf_part ]
else: else:
answer["result"] = [ 0 ] answer["result"] = [ 0 ]
elif cmd[0] == "set_config": elif cmd[0] == "set_config":
if len(cmd) == 3: if len(cmd) == 3:
# todo: use api (and check types?) # todo: use api (and check types?)
...@@ -143,20 +145,8 @@ class ConfigManager: ...@@ -143,20 +145,8 @@ class ConfigManager:
answer["result"] = [ 0 ] answer["result"] = [ 0 ]
else: else:
answer["result"] = [ 1, "Wrong number of arguments" ] answer["result"] = [ 1, "Wrong number of arguments" ]
elif cmd[0] == "zone" and cmd[1] == "add":
self.add_zone(cmd[2]) elif cmd[0] == "shutdown":
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":
print("[bind-cfgd] Received shutdown command") print("[bind-cfgd] Received shutdown command")
self.running = False self.running = False
else: else:
...@@ -171,11 +161,13 @@ class ConfigManager: ...@@ -171,11 +161,13 @@ class ConfigManager:
spec = msg["data_specification"] spec = msg["data_specification"]
if "config_data" in spec: if "config_data" in spec:
self.set_config(spec["module_name"], spec["config_data"]) 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: if "commands" in spec:
self.set_commands(spec["module_name"], spec["commands"]) 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 ] answer["result"] = [ 0 ]
elif 'result' in msg:
answer['result'] = [0]
else: else:
print("[bind-cfgd] unknown message: " + str(msg)) print("[bind-cfgd] unknown message: " + str(msg))
answer["result"] = [ 1, "Unknown module: " + str(msg) ] answer["result"] = [ 1, "Unknown module: " + str(msg) ]
......
...@@ -7,6 +7,13 @@ from command import BindCtlCmd ...@@ -7,6 +7,13 @@ from command import BindCtlCmd
from xml.dom import minidom from xml.dom import minidom
import ISC import ISC
import ISC.CC.data 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: try:
from collections import OrderedDict from collections import OrderedDict
...@@ -27,7 +34,7 @@ CONST_COMMAND_NODE = "command" ...@@ -27,7 +34,7 @@ CONST_COMMAND_NODE = "command"
class BindCtl(Cmd): class BindCtl(Cmd):
"""simple bindctl example.""" """simple bindctl example."""
def __init__(self, session = None): def __init__(self, server_port = 'localhost:8080'):
Cmd.__init__(self) Cmd.__init__(self)
self.location = "" self.location = ""
self.prompt_end = '> ' self.prompt_end = '> '
...@@ -35,13 +42,105 @@ class BindCtl(Cmd): ...@@ -35,13 +42,105 @@ class BindCtl(Cmd):
self.ruler = '-' self.ruler = '-'
self.modules = OrderedDict() self.modules = OrderedDict()
self.add_module_info(ModuleInfo("help", desc = "Get help for bindctl")) self.add_module_info(ModuleInfo("help", desc = "Get help for bindctl"))
self.cc = session self.server_port = server_port
self.config_data = ISC.CC.data.UIConfigData("", session) 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): def postcmd(self, stop, line):
self.prompt = self.location + self.prompt_end self.prompt = self.location + self.prompt_end
return stop 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): def validate_cmd(self, cmd):
if not cmd.module in self.modules: if not cmd.module in self.modules:
raise CmdUnknownModuleSyntaxError(cmd.module) raise CmdUnknownModuleSyntaxError(cmd.module)
...@@ -127,13 +226,7 @@ class BindCtl(Cmd): ...@@ -127,13 +226,7 @@ class BindCtl(Cmd):
def emptyline(self): def emptyline(self):
pass pass
def cmdloop(self):
try:
Cmd.cmdloop(self)
except KeyboardInterrupt:
return True
def do_help(self, name): def do_help(self, name):
print(CONST_BINDCTL_HELP) print(CONST_BINDCTL_HELP)
for k in self.modules.keys(): for k in self.modules.keys():
...@@ -141,9 +234,8 @@ class BindCtl(Cmd): ...@@ -141,9 +234,8 @@ class BindCtl(Cmd):
def onecmd(self, line): def onecmd(self, line):
# check if there's anything on the cc first
self.check_cc_messages()
if line == 'EOF' or line.lower() == "quit": if line == 'EOF' or line.lower() == "quit":
self.conn.close()
return True return True
if line == 'h': if line == 'h':
...@@ -170,8 +262,8 @@ class BindCtl(Cmd): ...@@ -170,8 +262,8 @@ class BindCtl(Cmd):
hints.extend([val for val in list if val.startswith(text)]) hints.extend([val for val in list if val.startswith(text)])
except CmdModuleNameFormatError: except CmdModuleNameFormatError:
if not text: if not text:
hints = list(self.modules.keys()) hints = self.get_module_names()
except CmdMissCommandNameFormatError as e: except CmdMissCommandNameFormatError as e:
if not text.strip(): # command name is empty if not text.strip(): # command name is empty
hints = self.modules[e.module].get_command_names() hints = self.modules[e.module].get_command_names()
...@@ -230,36 +322,8 @@ class BindCtl(Cmd): ...@@ -230,36 +322,8 @@ class BindCtl(Cmd):
return hint return hint
return [] 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): def _parse_cmd(self, line):
# check if there's anything on the cc first
self.check_cc_messages()
try: try:
cmd = BindCtlCmd(line) cmd = BindCtlCmd(line)
self.validate_cmd(cmd) self.validate_cmd(cmd)
...@@ -283,7 +347,6 @@ class BindCtl(Cmd): ...@@ -283,7 +347,6 @@ class BindCtl(Cmd):
def _append_space_to_hint(self): def _append_space_to_hint(self):
"""Append one space at the end of complete hint.""" """Append one space at the end of complete hint."""
self.hint = [(val + " ") for val in self.hint] self.hint = [(val + " ") for val in self.hint]
...@@ -330,7 +393,7 @@ class BindCtl(Cmd): ...@@ -330,7 +393,7 @@ class BindCtl(Cmd):
elif cmd.command == "revert": elif cmd.command == "revert":
self.config_data.revert() self.config_data.revert()
elif cmd.command == "commit": elif cmd.command == "commit":
self.config_data.commit(self.cc) self.config_data.commit(self)
elif cmd.command == "go": elif cmd.command == "go":
self.go(identifier) self.go(identifier)
except ISC.CC.data.DataTypeError as dte: except ISC.CC.data.DataTypeError as dte:
...@@ -353,29 +416,15 @@ class BindCtl(Cmd): ...@@ -353,29 +416,15 @@ class BindCtl(Cmd):
self.location = identifier self.location = identifier
def apply_cmd(self, cmd): def apply_cmd(self, cmd):
if not self.cc: url = '/' + cmd.module + '/' + cmd.command
return cmd_params = None
if (len(cmd.params) != 0):
groupName = cmd.module cmd_params = json.dumps(cmd.params)
content = [cmd.module, cmd.command]
values = cmd.params.values() print("send the message to cmd-ctrld")
if len(values) > 0: reply = self.send_POST(url, cmd.params)
content.append(list(values)[0]) data = reply.read().decode()
print("received reply:", data)
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)
......
...@@ -6,8 +6,8 @@ except ImportError: ...@@ -6,8 +6,8 @@ except ImportError:
from mycollections import OrderedDict from mycollections import OrderedDict
param_name_str = "^\s*(?P<param_name>[\w]+)\s*=\s*" param_name_str = "^\s*(?P<param_name>[\w]+)\s*=\s*"
param_value_str = "(?P<param_value>[\w\./]+)" param_value_str = "(?P<param_value>[\w\./-]+)"
param_value_with_quota_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>.*)$" next_params_str = "(?P<blank>\s*)(?P<comma>,?)(?P<next_params>.*)$"
PARAM_WITH_QUOTA_PATTERN = re.compile(param_name_str + PARAM_WITH_QUOTA_PATTERN = re.compile(param_name_str +
......
...@@ -17,7 +17,7 @@ def merge(orig, new): ...@@ -17,7 +17,7 @@ def merge(orig, new):
else: else:
orig[kn] = new[kn] orig[kn] = new[kn]
else: else:
orig.remove(kn) del orig[kn]
else: else:
orig[kn] = new[kn] orig[kn] = new[kn]
...@@ -180,63 +180,24 @@ class ConfigData: ...@@ -180,63 +180,24 @@ class ConfigData:
return None, False return None, False
class UIConfigData(): class UIConfigData():
def __init__(self, name, cc): def __init__(self, conn, name = ''):
self.module_name = 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.config = ConfigData(data_spec)
self.get_config_data(cc) self.get_config_data(conn)
self.config_changes = {} self.config_changes = {}
def get_config_data(self, cc): def get_config_data(self, conn):
cc.group_sendmsg({ "command": ["get_config", self.module_name] }, "ConfigManager") self.config.data = conn.send_GET('/config_data')
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 send_changes(self, cc): def send_changes(self, conn):
"""Sends the changes configuration values to the config manager. conn.send_POST('/ConfigManager/set_config', self.config_changes)
If the command succeeds, the changes are re-requested and # Get latest config data
the changed list is reset""" self.get_config_data(conn)
if self.module_name and self.module_name != "": self.config_changes = {}
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 get_data_specification(self, cc): def get_data_specification(self, conn):
cc.group_sendmsg({ "command": ["get_data_spec", self.module_name] }, "ConfigManager") return conn.send_GET('/config_spec')
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 set(self, identifier, value): def set(self, identifier, value):
# check against definition # check against definition
...@@ -371,5 +332,5 @@ class UIConfigData(): ...@@ -371,5 +332,5 @@ class UIConfigData():
def revert(self): def revert(self):
self.config_changes = {} self.config_changes = {}
def commit(self, cc): def commit(self, conn):
self.send_changes(cc) self.send_changes(conn)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment