mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-29 13:07:50 +00:00
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
This commit is contained in:
parent
e3e8cda089
commit
2019e5737c
@ -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)
|
||||
@ -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)
|
||||
|
||||
|
||||
|
||||
|
@ -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)
|
||||
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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user