Commit 1db7be22 authored by Jelte Jansen's avatar Jelte Jansen

Merged in branches/trac90

(module descriptions in spec files)
See also https://bind10.isc.org/ticket/90


git-svn-id: svn://bind10.isc.org/svn/bind10/trunk@1856 e5f2f494-b856-4b98-b285-d166d9295462
parent 80caa56c
{ {
"module_spec": { "module_spec": {
"module_name": "Auth", "module_name": "Auth",
"module_description": "Authoritative service",
"config_data": [ "config_data": [
{ "item_name": "database_file", { "item_name": "database_file",
"item_type": "string", "item_type": "string",
......
{ {
"module_spec": { "module_spec": {
"module_name": "Boss", "module_name": "Boss",
"module_description": "Master process",
"config_data": [ "config_data": [
{ {
"item_name": "example_string", "item_name": "example_string",
......
...@@ -180,13 +180,9 @@ class BindCmdInterpreter(Cmd): ...@@ -180,13 +180,9 @@ class BindCmdInterpreter(Cmd):
def _update_commands(self): def _update_commands(self):
'''Get the commands of all modules. ''' '''Update the commands of all modules. '''
cmd_spec = self.send_GET('/command_spec') for module_name in self.config_data.get_config_item_list():
if not cmd_spec: self._prepare_module_commands(self.config_data.get_module_spec(module_name))
return
for module_name in cmd_spec.keys():
self._prepare_module_commands(module_name, cmd_spec[module_name])
def send_GET(self, url, body = None): def send_GET(self, url, body = None):
'''Send GET request to cmdctl, session id is send with the name '''Send GET request to cmdctl, session id is send with the name
...@@ -222,11 +218,11 @@ class BindCmdInterpreter(Cmd): ...@@ -222,11 +218,11 @@ class BindCmdInterpreter(Cmd):
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): def _prepare_module_commands(self, module_spec):
'''Prepare the module commands''' '''Prepare the module commands'''
module = ModuleInfo(name = module_name, module = ModuleInfo(name = module_spec.get_module_name(),
desc = "same here") desc = module_spec.get_module_description())
for command in module_commands: for command in module_spec.get_commands_spec():
cmd = CommandInfo(name = command["command_name"], cmd = CommandInfo(name = command["command_name"],
desc = command["command_description"]) desc = command["command_description"])
for arg in command["command_args"]: for arg in command["command_args"]:
......
...@@ -219,8 +219,7 @@ class CommandControl(): ...@@ -219,8 +219,7 @@ class CommandControl():
self._verbose = verbose self._verbose = verbose
self.cc = isc.cc.Session() self.cc = isc.cc.Session()
self.cc.group_subscribe('Cmd-Ctrld') self.cc.group_subscribe('Cmd-Ctrld')
self.command_spec = self.get_cmd_specification() self.module_spec = self.get_module_specification()
self.config_spec = self.get_data_specification()
self.config_data = self.get_config_data() self.config_data = self.get_config_data()
def _parse_command_result(self, rcode, reply): def _parse_command_result(self, rcode, reply):
...@@ -229,10 +228,6 @@ class CommandControl(): ...@@ -229,10 +228,6 @@ class CommandControl():
return {} return {}
return reply return reply
def get_cmd_specification(self):
rcode, reply = self.send_command('ConfigManager', isc.config.ccsession.COMMAND_GET_COMMANDS_SPEC)
return self._parse_command_result(rcode, reply)
def get_config_data(self): def get_config_data(self):
'''Get config data for all modules from configmanager ''' '''Get config data for all modules from configmanager '''
rcode, reply = self.send_command('ConfigManager', isc.config.ccsession.COMMAND_GET_CONFIG) rcode, reply = self.send_command('ConfigManager', isc.config.ccsession.COMMAND_GET_CONFIG)
...@@ -244,7 +239,7 @@ class CommandControl(): ...@@ -244,7 +239,7 @@ class CommandControl():
if module_name == 'ConfigManager' and command_name == isc.config.ccsession.COMMAND_SET_CONFIG: if module_name == 'ConfigManager' and command_name == isc.config.ccsession.COMMAND_SET_CONFIG:
self.config_data = self.get_config_data() self.config_data = self.get_config_data()
def get_data_specification(self): def get_module_specification(self):
rcode, reply = self.send_command('ConfigManager', isc.config.ccsession.COMMAND_GET_MODULE_SPEC) rcode, reply = self.send_command('ConfigManager', isc.config.ccsession.COMMAND_GET_MODULE_SPEC)
return self._parse_command_result(rcode, reply) return self._parse_command_result(rcode, reply)
...@@ -253,10 +248,8 @@ class CommandControl(): ...@@ -253,10 +248,8 @@ class CommandControl():
(message, env) = self.cc.group_recvmsg(True) (message, env) = self.cc.group_recvmsg(True)
command, arg = isc.config.ccsession.parse_command(message) command, arg = isc.config.ccsession.parse_command(message)
while command: while command:
if command == isc.config.ccsession.COMMAND_COMMANDS_UPDATE: if command == isc.config.ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE:
self.command_spec[arg[0]] = arg[1] self.module_spec[arg[0]] = arg[1]
elif command == isc.config.ccsession.COMMAND_SPECIFICATION_UPDATE:
self.config_spec[arg[0]] = arg[1]
elif command == isc.config.ccsession.COMMAND_SHUTDOWN: elif command == isc.config.ccsession.COMMAND_SHUTDOWN:
return False return False
(message, env) = self.cc.group_recvmsg(True) (message, env) = self.cc.group_recvmsg(True)
...@@ -278,11 +271,12 @@ class CommandControl(): ...@@ -278,11 +271,12 @@ class CommandControl():
if module_name == 'ConfigManager': if module_name == 'ConfigManager':
return self.send_command(module_name, command_name, params) return self.send_command(module_name, command_name, params)
if module_name not in self.command_spec.keys(): if module_name not in self.module_spec.keys():
return 1, {'error' : 'unknown module'} return 1, {'error' : 'unknown module'}
cmd_valid = False cmd_valid = False
commands = self.command_spec[module_name] # todo: make validate_command() in ModuleSpec class
commands = self.module_spec[module_name]["commands"]
for cmd in commands: for cmd in commands:
if cmd['command_name'] == command_name: if cmd['command_name'] == command_name:
cmd_valid = True cmd_valid = True
...@@ -383,12 +377,10 @@ class SecureHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer): ...@@ -383,12 +377,10 @@ class SecureHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
'''Currently only support the following three url GET request ''' '''Currently only support the following three url GET request '''
rcode, reply = http.client.NO_CONTENT, [] rcode, reply = http.client.NO_CONTENT, []
if not module: if not module:
if id == 'command_spec': if id == 'config_data':
rcode, reply = http.client.OK, self.cmdctrl.command_spec
elif id == 'config_data':
rcode, reply = http.client.OK, self.cmdctrl.config_data rcode, reply = http.client.OK, self.cmdctrl.config_data
elif id == 'config_spec': elif id == 'module_spec':
rcode, reply = http.client.OK, self.cmdctrl.config_spec rcode, reply = http.client.OK, self.cmdctrl.module_spec
return rcode, reply return rcode, reply
......
{ {
"module_spec": { "module_spec": {
"module_name": "Cmdctl", "module_name": "Cmdctl",
"module_description": "Interface for command and control",
"config_data": [ "config_data": [
{ {
"item_name": "key_file", "item_name": "key_file",
......
{ {
"module_spec": { "module_spec": {
"module_name": "Xfrin", "module_name": "Xfrin",
"module_description": "XFR in daemon",
"config_data": [ "config_data": [
{ {
"item_name": "transfers_in", "item_name": "transfers_in",
......
...@@ -46,3 +46,5 @@ EXTRA_DIST += testdata/spec21.spec ...@@ -46,3 +46,5 @@ EXTRA_DIST += testdata/spec21.spec
EXTRA_DIST += testdata/spec22.spec EXTRA_DIST += testdata/spec22.spec
EXTRA_DIST += testdata/spec23.spec EXTRA_DIST += testdata/spec23.spec
EXTRA_DIST += testdata/spec24.spec EXTRA_DIST += testdata/spec24.spec
EXTRA_DIST += testdata/spec25.spec
EXTRA_DIST += testdata/spec26.spec
...@@ -145,6 +145,7 @@ check_command_list(const ElementPtr& spec) { ...@@ -145,6 +145,7 @@ check_command_list(const ElementPtr& spec) {
static void static void
check_data_specification(const ElementPtr& spec) { check_data_specification(const ElementPtr& spec) {
check_leaf_item(spec, "module_name", Element::string, true); check_leaf_item(spec, "module_name", Element::string, true);
check_leaf_item(spec, "module_description", Element::string, false);
// config_data is not mandatory; module could just define // config_data is not mandatory; module could just define
// commands and have no config // commands and have no config
if (spec->contains("config_data")) { if (spec->contains("config_data")) {
...@@ -204,6 +205,16 @@ ModuleSpec::getModuleName() const ...@@ -204,6 +205,16 @@ ModuleSpec::getModuleName() const
return module_specification->get("module_name")->stringValue(); return module_specification->get("module_name")->stringValue();
} }
const std::string
ModuleSpec::getModuleDescription() const
{
if (module_specification->contains("module_description")) {
return module_specification->get("module_description")->stringValue();
} else {
return std::string("");
}
}
bool bool
ModuleSpec::validate_config(const ElementPtr data, const bool full) ModuleSpec::validate_config(const ElementPtr data, const bool full)
{ {
......
...@@ -77,6 +77,10 @@ namespace isc { namespace config { ...@@ -77,6 +77,10 @@ namespace isc { namespace config {
/// Returns the module name as specified by the specification /// Returns the module name as specified by the specification
const std::string getModuleName() const; const std::string getModuleName() const;
/// Returns the module description as specified by the specification
/// returns an empty string if there is no description
const std::string getModuleDescription() const;
// returns true if the given element conforms to this data // returns true if the given element conforms to this data
// configuration specification // configuration specification
/// Validates the given configuration data for this specification. /// Validates the given configuration data for this specification.
......
{
"module_spec": {
"module_name": "Spec25",
"module_description": "Just an empty module"
}
}
{
"module_spec": {
"module_name": "Spec26",
"module_description": 1
}
}
...@@ -60,6 +60,12 @@ TEST(ModuleSpec, ReadingSpecfiles) { ...@@ -60,6 +60,12 @@ TEST(ModuleSpec, ReadingSpecfiles) {
dd = moduleSpecFromFile(specfile("spec2.spec")); dd = moduleSpecFromFile(specfile("spec2.spec"));
EXPECT_EQ("[ {\"command_args\": [ {\"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": False, \"item_type\": \"string\"} ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\"}, {\"command_args\": [ ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\"} ]", dd.getCommandsSpec()->str()); EXPECT_EQ("[ {\"command_args\": [ {\"item_default\": \"\", \"item_name\": \"message\", \"item_optional\": False, \"item_type\": \"string\"} ], \"command_description\": \"Print the given message to stdout\", \"command_name\": \"print_message\"}, {\"command_args\": [ ], \"command_description\": \"Shut down BIND 10\", \"command_name\": \"shutdown\"} ]", dd.getCommandsSpec()->str());
EXPECT_EQ("Spec2", dd.getModuleName()); EXPECT_EQ("Spec2", dd.getModuleName());
EXPECT_EQ("", dd.getModuleDescription());
dd = moduleSpecFromFile(specfile("spec25.spec"));
EXPECT_EQ("Spec25", dd.getModuleName());
EXPECT_EQ("Just an empty module", dd.getModuleDescription());
EXPECT_THROW(moduleSpecFromFile(specfile("spec26.spec")), ModuleSpecError);
std::ifstream file; std::ifstream file;
file.open(specfile("spec1.spec").c_str()); file.open(specfile("spec1.spec").c_str());
......
...@@ -81,8 +81,7 @@ def create_answer(rcode, arg = None): ...@@ -81,8 +81,7 @@ def create_answer(rcode, arg = None):
# 'fixed' commands # 'fixed' commands
"""Fixed names for command and configuration messages""" """Fixed names for command and configuration messages"""
COMMAND_CONFIG_UPDATE = "config_update" COMMAND_CONFIG_UPDATE = "config_update"
COMMAND_COMMANDS_UPDATE = "commands_update" COMMAND_MODULE_SPECIFICATION_UPDATE = "module_specification_update"
COMMAND_SPECIFICATION_UPDATE = "specification_update"
COMMAND_GET_COMMANDS_SPEC = "get_commands_spec" COMMAND_GET_COMMANDS_SPEC = "get_commands_spec"
COMMAND_GET_CONFIG = "get_config" COMMAND_GET_CONFIG = "get_config"
...@@ -314,16 +313,9 @@ class UIModuleCCSession(MultiConfigData): ...@@ -314,16 +313,9 @@ class UIModuleCCSession(MultiConfigData):
# this step should be unnecessary but is the current way cmdctl returns stuff # this step should be unnecessary but is the current way cmdctl returns stuff
# so changes are needed there to make this clean (we need a command to simply get the # so changes are needed there to make this clean (we need a command to simply get the
# full specs for everything, including commands etc, not separate gets for that) # full specs for everything, including commands etc, not separate gets for that)
specs = self._conn.send_GET('/config_spec') specs = self._conn.send_GET('/module_spec')
commands = self._conn.send_GET('/commands')
for module in specs.keys(): for module in specs.keys():
cur_spec = { 'module_name': module } self.set_specification(isc.config.ModuleSpec(specs[module]))
if module in specs and specs[module]:
cur_spec['config_data'] = specs[module]
if module in commands and commands[module]:
cur_spec['commands'] = commands[module]
self.set_specification(isc.config.ModuleSpec(cur_spec))
def request_current_config(self): def request_current_config(self):
"""Requests the current configuration from the configuration """Requests the current configuration from the configuration
......
...@@ -148,11 +148,23 @@ class ConfigManager: ...@@ -148,11 +148,23 @@ class ConfigManager:
if module_name in self.module_specs: if module_name in self.module_specs:
del self.module_specs[module_name] del self.module_specs[module_name]
def get_module_spec(self, module_name): def get_module_spec(self, module_name = None):
"""Returns the full ModuleSpec for the module with the given """Returns the full ModuleSpec for the module with the given
module_name""" module_name. If no module name is given, a dict will
if module_name in self.module_specs: be returned with 'name': module_spec values. If the
return self.module_specs[module_name] module name is given, but does not exist, an empty dict
is returned"""
if module_name:
if module_name in self.module_specs:
return self.module_specs[module_name]
else:
# TODO: log error?
return {}
else:
result = {}
for module in self.module_specs:
result[module] = self.module_specs[module].get_full_spec()
return result
def get_config_spec(self, name = None): def get_config_spec(self, name = None):
"""Returns a dict containing 'module_name': config_spec for """Returns a dict containing 'module_name': config_spec for
...@@ -201,13 +213,13 @@ class ConfigManager: ...@@ -201,13 +213,13 @@ class ConfigManager:
if type(cmd) == dict: if type(cmd) == dict:
if 'module_name' in cmd and cmd['module_name'] != '': if 'module_name' in cmd and cmd['module_name'] != '':
module_name = cmd['module_name'] module_name = cmd['module_name']
answer = isc.config.ccsession.create_answer(0, self.get_config_spec(module_name)) answer = isc.config.ccsession.create_answer(0, self.get_module_spec(module_name))
else: else:
answer = isc.config.ccsession.create_answer(1, "Bad module_name in get_module_spec command") answer = isc.config.ccsession.create_answer(1, "Bad module_name in get_module_spec command")
else: else:
answer = isc.config.ccsession.create_answer(1, "Bad get_module_spec command, argument not a dict") answer = isc.config.ccsession.create_answer(1, "Bad get_module_spec command, argument not a dict")
else: else:
answer = isc.config.ccsession.create_answer(0, self.get_config_spec()) answer = isc.config.ccsession.create_answer(0, self.get_module_spec())
return answer return answer
def _handle_get_config(self, cmd): def _handle_get_config(self, cmd):
...@@ -303,14 +315,10 @@ class ConfigManager: ...@@ -303,14 +315,10 @@ class ConfigManager:
# We should make one general 'spec update for module' that # We should make one general 'spec update for module' that
# passes both specification and commands at once # passes both specification and commands at once
spec_update = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_SPECIFICATION_UPDATE, spec_update = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE,
[ spec.get_module_name(), spec.get_config_spec() ]) [ spec.get_module_name(), spec.get_full_spec() ])
self.cc.group_sendmsg(spec_update, "Cmd-Ctrld") self.cc.group_sendmsg(spec_update, "Cmd-Ctrld")
cmds_update = isc.config.ccsession.create_command(isc.config.ccsession.COMMAND_COMMANDS_UPDATE, return isc.config.ccsession.create_answer(0)
[ spec.get_module_name(), spec.get_commands_spec() ])
self.cc.group_sendmsg(cmds_update, "Cmd-Ctrld")
answer = isc.config.ccsession.create_answer(0)
return answer
def handle_msg(self, msg): def handle_msg(self, msg):
"""Handle a command from the cc channel to the configuration manager""" """Handle a command from the cc channel to the configuration manager"""
......
...@@ -86,9 +86,19 @@ class ModuleSpec: ...@@ -86,9 +86,19 @@ class ModuleSpec:
def get_module_name(self): def get_module_name(self):
"""Returns a string containing the name of the module as """Returns a string containing the name of the module as
specified by the specification given at __init__""" specified by the specification given at __init__()"""
return self._module_spec['module_name'] return self._module_spec['module_name']
def get_module_description(self):
"""Returns a string containing the description of the module as
specified by the specification given at __init__().
Returns an empty string if there is no description.
"""
if 'module_description' in self._module_spec:
return self._module_spec['module_description']
else:
return ""
def get_full_spec(self): def get_full_spec(self):
"""Returns a dict representation of the full module specification""" """Returns a dict representation of the full module specification"""
return self._module_spec return self._module_spec
...@@ -123,6 +133,9 @@ def _check(module_spec): ...@@ -123,6 +133,9 @@ def _check(module_spec):
raise ModuleSpecError("data specification not a dict") raise ModuleSpecError("data specification not a dict")
if "module_name" not in module_spec: if "module_name" not in module_spec:
raise ModuleSpecError("no module_name in module_spec") raise ModuleSpecError("no module_name in module_spec")
if "module_description" in module_spec and \
type(module_spec["module_description"]) != str:
raise ModuleSpecError("module_description is not a string")
if "config_data" in module_spec: if "config_data" in module_spec:
_check_config_spec(module_spec["config_data"]) _check_config_spec(module_spec["config_data"])
if "commands" in module_spec: if "commands" in module_spec:
......
...@@ -75,6 +75,7 @@ class TestModuleSpec(unittest.TestCase): ...@@ -75,6 +75,7 @@ class TestModuleSpec(unittest.TestCase):
self.assertRaises(ModuleSpecError, self.read_spec_file, "spec19.spec") self.assertRaises(ModuleSpecError, self.read_spec_file, "spec19.spec")
self.assertRaises(ModuleSpecError, self.read_spec_file, "spec20.spec") self.assertRaises(ModuleSpecError, self.read_spec_file, "spec20.spec")
self.assertRaises(ModuleSpecError, self.read_spec_file, "spec21.spec") self.assertRaises(ModuleSpecError, self.read_spec_file, "spec21.spec")
self.assertRaises(ModuleSpecError, self.read_spec_file, "spec26.spec")
def validate_data(self, specfile_name, datafile_name): def validate_data(self, specfile_name, datafile_name):
dd = self.read_spec_file(specfile_name); dd = self.read_spec_file(specfile_name);
...@@ -98,6 +99,10 @@ class TestModuleSpec(unittest.TestCase): ...@@ -98,6 +99,10 @@ class TestModuleSpec(unittest.TestCase):
module_spec = isc.config.module_spec_from_file(self.spec_file("spec1.spec"), False) module_spec = isc.config.module_spec_from_file(self.spec_file("spec1.spec"), False)
self.spec1(module_spec) self.spec1(module_spec)
module_spec = isc.config.module_spec_from_file(self.spec_file("spec25.spec"), True)
self.assertEqual("Spec25", module_spec.get_module_name())
self.assertEqual("Just an empty module", module_spec.get_module_description())
def test_str(self): def test_str(self):
module_spec = isc.config.module_spec_from_file(self.spec_file("spec1.spec"), False) module_spec = isc.config.module_spec_from_file(self.spec_file("spec1.spec"), False)
self.assertEqual(module_spec.__str__(), "{'module_name': 'Spec1'}") self.assertEqual(module_spec.__str__(), "{'module_name': 'Spec1'}")
......
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