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
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
......
......@@ -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) ]
......
......@@ -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)
......@@ -128,12 +227,6 @@ 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,7 +262,7 @@ 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
......@@ -231,35 +323,7 @@ class BindCtl(Cmd):
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)
......
......@@ -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 +
......
......@@ -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)
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 = {}
else:
print("Error committing changes: " + answer['result'][1])
else:
print("Error: unexpected answer: " + str(answer))
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)
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