Commit 6b75c128 authored by Jelte Jansen's avatar Jelte Jansen
Browse files

Merge branch 'trac1290'

parents 8cea64b6 1a5bd80b
......@@ -991,6 +991,7 @@ AC_OUTPUT([doc/version.ent
src/lib/util/python/mkpywrapper.py
src/lib/util/python/gen_wiredata.py
src/lib/server_common/tests/data_path.h
tests/lettuce/setup_intree_bind10.sh
tests/system/conf.sh
tests/system/run.sh
tests/system/glue/setup.sh
......
......@@ -675,6 +675,8 @@ class BoB:
args = ["b10-cmdctl"]
if self.cmdctl_port is not None:
args.append("--port=" + str(self.cmdctl_port))
if self.verbose:
args.append("-v")
self.start_process("b10-cmdctl", args, c_channel_env, self.cmdctl_port)
def start_all_processes(self):
......
......@@ -45,6 +45,5 @@ export B10_FROM_BUILD
BIND10_MSGQ_SOCKET_FILE=@abs_top_builddir@/msgq_socket
export BIND10_MSGQ_SOCKET_FILE
cd ${BIND10_PATH}
exec ${PYTHON_EXEC} -O bind10 "$@"
exec ${PYTHON_EXEC} -O ${BIND10_PATH}/bind10 "$@"
......@@ -71,21 +71,21 @@ Type \"<module_name> <command_name> help\" for help on the specific command.
\nAvailable module names: """
class ValidatedHTTPSConnection(http.client.HTTPSConnection):
'''Overrides HTTPSConnection to support certification
'''Overrides HTTPSConnection to support certification
validation. '''
def __init__(self, host, ca_certs):
http.client.HTTPSConnection.__init__(self, host)
self.ca_certs = ca_certs
def connect(self):
''' Overrides the connect() so that we do
''' Overrides the connect() so that we do
certificate validation. '''
sock = socket.create_connection((self.host, self.port),
self.timeout)
if self._tunnel_host:
self.sock = sock
self._tunnel()
req_cert = ssl.CERT_NONE
if self.ca_certs:
req_cert = ssl.CERT_REQUIRED
......@@ -95,7 +95,7 @@ class ValidatedHTTPSConnection(http.client.HTTPSConnection):
ca_certs=self.ca_certs)
class BindCmdInterpreter(Cmd):
"""simple bindctl example."""
"""simple bindctl example."""
def __init__(self, server_port='localhost:8080', pem_file=None,
csv_file_dir=None):
......@@ -128,29 +128,33 @@ class BindCmdInterpreter(Cmd):
socket.gethostname())).encode())
digest = session_id.hexdigest()
return digest
def run(self):
'''Parse commands from user and send them to cmdctl. '''
try:
if not self.login_to_cmdctl():
return
return 1
self.cmdloop()
print('\nExit from bindctl')
return 0
except FailToLogin as err:
# error already printed when this was raised, ignoring
pass
return 1
except KeyboardInterrupt:
print('\nExit from bindctl')
return 0
except socket.error as err:
print('Failed to send request, the connection is closed')
return 1
except http.client.CannotSendRequest:
print('Can not send request, the connection is busy')
return 1
def _get_saved_user_info(self, dir, file_name):
''' Read all the available username and password pairs saved in
''' Read all the available username and password pairs saved in
file(path is "dir + file_name"), Return value is one list of elements
['name', 'password'], If get information failed, empty list will be
['name', 'password'], If get information failed, empty list will be
returned.'''
if (not dir) or (not os.path.exists(dir)):
return []
......@@ -176,7 +180,7 @@ class BindCmdInterpreter(Cmd):
if not os.path.exists(dir):
os.mkdir(dir, 0o700)
csvfilepath = dir + file_name
csvfilepath = dir + file_name
csvfile = open(csvfilepath, 'w')
os.chmod(csvfilepath, 0o600)
writer = csv.writer(csvfile)
......@@ -190,7 +194,7 @@ class BindCmdInterpreter(Cmd):
return True
def login_to_cmdctl(self):
'''Login to cmdctl with the username and password inputted
'''Login to cmdctl with the username and password inputted
from user. After the login is sucessful, the username and
password will be saved in 'default_user.csv', when run the next
time, username and password saved in 'default_user.csv' will be
......@@ -256,14 +260,14 @@ class BindCmdInterpreter(Cmd):
if self.login_to_cmdctl():
# successful, so try send again
status, reply_msg = self._send_message(url, body)
if reply_msg:
return json.loads(reply_msg.decode())
else:
return {}
def send_POST(self, url, post_param = None):
def send_POST(self, url, post_param = None):
'''Send POST request to cmdctl, session id is send with the name
'cookie' in header.
Format: /module_name/command_name
......@@ -322,12 +326,12 @@ class BindCmdInterpreter(Cmd):
def _validate_cmd(self, cmd):
'''validate the parameters and merge some parameters together,
merge algorithm is based on the command line syntax, later, if
a better command line syntax come out, this function should be
updated first.
a better command line syntax come out, this function should be
updated first.
'''
if not cmd.module in self.modules:
raise CmdUnknownModuleSyntaxError(cmd.module)
module_info = self.modules[cmd.module]
if not module_info.has_command_with_name(cmd.command):
raise CmdUnknownCmdSyntaxError(cmd.module, cmd.command)
......@@ -335,17 +339,17 @@ class BindCmdInterpreter(Cmd):
command_info = module_info.get_command_with_name(cmd.command)
manda_params = command_info.get_mandatory_param_names()
all_params = command_info.get_param_names()
# If help is entered, don't do further parameter validation.
for val in cmd.params.keys():
if val == "help":
return
params = cmd.params.copy()
if not params and manda_params:
raise CmdMissParamSyntaxError(cmd.module, cmd.command, manda_params[0])
params = cmd.params.copy()
if not params and manda_params:
raise CmdMissParamSyntaxError(cmd.module, cmd.command, manda_params[0])
elif params and not all_params:
raise CmdUnknownParamSyntaxError(cmd.module, cmd.command,
raise CmdUnknownParamSyntaxError(cmd.module, cmd.command,
list(params.keys())[0])
elif params:
param_name = None
......@@ -376,7 +380,7 @@ class BindCmdInterpreter(Cmd):
param_name = command_info.get_param_name_by_position(name, param_count)
cmd.params[param_name] = cmd.params[name]
del cmd.params[name]
elif not name in all_params:
raise CmdUnknownParamSyntaxError(cmd.module, cmd.command, name)
......@@ -385,7 +389,7 @@ class BindCmdInterpreter(Cmd):
if not name in params and not param_nr in params:
raise CmdMissParamSyntaxError(cmd.module, cmd.command, name)
param_nr += 1
# Convert parameter value according parameter spec file.
# Ignore check for commands belongs to module 'config'
if cmd.module != CONFIG_MODULE_NAME:
......@@ -394,9 +398,9 @@ class BindCmdInterpreter(Cmd):
try:
cmd.params[param_name] = isc.config.config_data.convert_type(param_spec, cmd.params[param_name])
except isc.cc.data.DataTypeError as e:
raise isc.cc.data.DataTypeError('Invalid parameter value for \"%s\", the type should be \"%s\" \n'
raise isc.cc.data.DataTypeError('Invalid parameter value for \"%s\", the type should be \"%s\" \n'
% (param_name, param_spec['item_type']) + str(e))
def _handle_cmd(self, cmd):
'''Handle a command entered by the user'''
if cmd.command == "help" or ("help" in cmd.params.keys()):
......@@ -418,7 +422,7 @@ class BindCmdInterpreter(Cmd):
def add_module_info(self, module_info):
'''Add the information about one module'''
self.modules[module_info.name] = module_info
def get_module_names(self):
'''Return the names of all known modules'''
return list(self.modules.keys())
......@@ -450,15 +454,15 @@ class BindCmdInterpreter(Cmd):
subsequent_indent=" " +
" " * CONST_BINDCTL_HELP_INDENT_WIDTH,
width=70))
def onecmd(self, line):
if line == 'EOF' or line.lower() == "quit":
self.conn.close()
return True
if line == 'h':
line = 'help'
Cmd.onecmd(self, line)
def remove_prefix(self, list, prefix):
......@@ -486,7 +490,7 @@ class BindCmdInterpreter(Cmd):
cmd = BindCmdParse(cur_line)
if not cmd.params and text:
hints = self._get_command_startswith(cmd.module, text)
else:
else:
hints = self._get_param_startswith(cmd.module, cmd.command,
text)
if cmd.module == CONFIG_MODULE_NAME:
......@@ -502,8 +506,8 @@ class BindCmdInterpreter(Cmd):
except CmdMissCommandNameFormatError as e:
if not text.strip(): # command name is empty
hints = self.modules[e.module].get_command_names()
else:
hints = self.modules[e.module].get_command_names()
else:
hints = self._get_module_startswith(text)
except CmdCommandNameFormatError as e:
......@@ -523,36 +527,37 @@ class BindCmdInterpreter(Cmd):
else:
return None
def _get_module_startswith(self, text):
def _get_module_startswith(self, text):
return [module
for module in self.modules
for module in self.modules
if module.startswith(text)]
def _get_command_startswith(self, module, text):
if module in self.modules:
if module in self.modules:
return [command
for command in self.modules[module].get_command_names()
for command in self.modules[module].get_command_names()
if command.startswith(text)]
return []
def _get_param_startswith(self, module, command, text):
return []
def _get_param_startswith(self, module, command, text):
if module in self.modules:
module_info = self.modules[module]
if command in module_info.get_command_names():
module_info = self.modules[module]
if command in module_info.get_command_names():
cmd_info = module_info.get_command_with_name(command)
params = cmd_info.get_param_names()
params = cmd_info.get_param_names()
hint = []
if text:
if text:
hint = [val for val in params if val.startswith(text)]
else:
hint = list(params)
if len(hint) == 1 and hint[0] != "help":
hint[0] = hint[0] + " ="
hint[0] = hint[0] + " ="
return hint
return []
......@@ -569,24 +574,24 @@ class BindCmdInterpreter(Cmd):
self._print_correct_usage(err)
except isc.cc.data.DataTypeError as err:
print("Error! ", err)
def _print_correct_usage(self, ept):
def _print_correct_usage(self, ept):
if isinstance(ept, CmdUnknownModuleSyntaxError):
self.do_help(None)
elif isinstance(ept, CmdUnknownCmdSyntaxError):
self.modules[ept.module].module_help()
elif isinstance(ept, CmdMissParamSyntaxError) or \
isinstance(ept, CmdUnknownParamSyntaxError):
self.modules[ept.module].command_help(ept.command)
def _append_space_to_hint(self):
"""Append one space at the end of complete hint."""
self.hint = [(val + " ") for val in self.hint]
def _handle_help(self, cmd):
if cmd.command == "help":
self.modules[cmd.module].module_help()
......
......@@ -146,4 +146,5 @@ if __name__ == '__main__':
tool = BindCmdInterpreter(server_addr, pem_file=options.cert_chain,
csv_file_dir=options.csv_file_dir)
prepare_config_commands(tool)
tool.run()
result = tool.run()
sys.exit(result)
......@@ -31,14 +31,14 @@ from bindctl_main import set_bindctl_options
from bindctl import cmdparse
from bindctl import bindcmd
from bindctl.moduleinfo import *
from bindctl.exception import *
from bindctl.exception import *
try:
from collections import OrderedDict
except ImportError:
from mycollections import OrderedDict
class TestCmdLex(unittest.TestCase):
def my_assert_raise(self, exception_type, cmd_line):
self.assertRaises(exception_type, cmdparse.BindCmdParse, cmd_line)
......@@ -48,13 +48,13 @@ class TestCmdLex(unittest.TestCase):
assert cmd.module == "zone"
assert cmd.command == "add"
self.assertEqual(len(cmd.params), 0)
def testCommandWithParameters(self):
lines = {"zone add zone_name = cnnic.cn, file = cnnic.cn.file master=1.1.1.1",
"zone add zone_name = \"cnnic.cn\", file ='cnnic.cn.file' master=1.1.1.1 ",
"zone add zone_name = 'cnnic.cn\", file ='cnnic.cn.file' master=1.1.1.1, " }
for cmd_line in lines:
cmd = cmdparse.BindCmdParse(cmd_line)
assert cmd.module == "zone"
......@@ -75,7 +75,7 @@ class TestCmdLex(unittest.TestCase):
cmd = cmdparse.BindCmdParse('zone cmd name = 1\"\'34**&2 ,value= 44\"\'\"')
self.assertEqual(cmd.params['name'], '1\"\'34**&2')
self.assertEqual(cmd.params['value'], '44\"\'\"')
cmd = cmdparse.BindCmdParse('zone cmd name = 1\'34**&2value=44\"\'\" value = \"==============\'')
self.assertEqual(cmd.params['name'], '1\'34**&2value=44\"\'\"')
self.assertEqual(cmd.params['value'], '==============')
......@@ -83,34 +83,34 @@ class TestCmdLex(unittest.TestCase):
cmd = cmdparse.BindCmdParse('zone cmd name = \"1234, 567890 \" value ==&*/')
self.assertEqual(cmd.params['name'], '1234, 567890 ')
self.assertEqual(cmd.params['value'], '=&*/')
def testCommandWithListParam(self):
cmd = cmdparse.BindCmdParse("zone set zone_name='cnnic.cn', master='1.1.1.1, 2.2.2.2'")
assert cmd.params["master"] == '1.1.1.1, 2.2.2.2'
assert cmd.params["master"] == '1.1.1.1, 2.2.2.2'
def testCommandWithHelpParam(self):
cmd = cmdparse.BindCmdParse("zone add help")
assert cmd.params["help"] == "help"
cmd = cmdparse.BindCmdParse("zone add help *&)&)*&&$#$^%")
assert cmd.params["help"] == "help"
self.assertEqual(len(cmd.params), 1)
def testCmdModuleNameFormatError(self):
self.my_assert_raise(CmdModuleNameFormatError, "zone=good")
self.my_assert_raise(CmdModuleNameFormatError, "zo/ne")
self.my_assert_raise(CmdModuleNameFormatError, "")
self.my_assert_raise(CmdModuleNameFormatError, "zo/ne")
self.my_assert_raise(CmdModuleNameFormatError, "")
self.my_assert_raise(CmdModuleNameFormatError, "=zone")
self.my_assert_raise(CmdModuleNameFormatError, "zone,")
self.my_assert_raise(CmdModuleNameFormatError, "zone,")
def testCmdMissCommandNameFormatError(self):
self.my_assert_raise(CmdMissCommandNameFormatError, "zone")
self.my_assert_raise(CmdMissCommandNameFormatError, "zone ")
self.my_assert_raise(CmdMissCommandNameFormatError, "help ")
def testCmdCommandNameFormatError(self):
self.my_assert_raise(CmdCommandNameFormatError, "zone =d")
self.my_assert_raise(CmdCommandNameFormatError, "zone z=d")
......@@ -119,11 +119,11 @@ class TestCmdLex(unittest.TestCase):
self.my_assert_raise(CmdCommandNameFormatError, "zone zdd/ \"")
class TestCmdSyntax(unittest.TestCase):
def _create_bindcmd(self):
"""Create one bindcmd"""
tool = bindcmd.BindCmdInterpreter()
tool = bindcmd.BindCmdInterpreter()
string_spec = { 'item_type' : 'string',
'item_optional' : False,
'item_default' : ''}
......@@ -135,40 +135,40 @@ class TestCmdSyntax(unittest.TestCase):
load_cmd = CommandInfo(name = "load")
load_cmd.add_param(zone_file_param)
load_cmd.add_param(zone_name)
param_master = ParamInfo(name = "master", optional = True, param_spec = string_spec)
param_master = ParamInfo(name = "port", optional = True, param_spec = int_spec)
param_allow_update = ParamInfo(name = "allow_update", optional = True, param_spec = string_spec)
param_master = ParamInfo(name = "master", optional = True, param_spec = string_spec)
param_master = ParamInfo(name = "port", optional = True, param_spec = int_spec)
param_allow_update = ParamInfo(name = "allow_update", optional = True, param_spec = string_spec)
set_cmd = CommandInfo(name = "set")
set_cmd.add_param(param_master)
set_cmd.add_param(param_allow_update)
set_cmd.add_param(zone_name)
reload_all_cmd = CommandInfo(name = "reload_all")
zone_module = ModuleInfo(name = "zone")
reload_all_cmd = CommandInfo(name = "reload_all")
zone_module = ModuleInfo(name = "zone")
zone_module.add_command(load_cmd)
zone_module.add_command(set_cmd)
zone_module.add_command(reload_all_cmd)
tool.add_module_info(zone_module)
return tool
def setUp(self):
self.bindcmd = self._create_bindcmd()
def no_assert_raise(self, cmd_line):
cmd = cmdparse.BindCmdParse(cmd_line)
self.bindcmd._validate_cmd(cmd)
self.bindcmd._validate_cmd(cmd)
def my_assert_raise(self, exception_type, cmd_line):
cmd = cmdparse.BindCmdParse(cmd_line)
self.assertRaises(exception_type, self.bindcmd._validate_cmd, cmd)
self.assertRaises(exception_type, self.bindcmd._validate_cmd, cmd)
def testValidateSuccess(self):
self.no_assert_raise("zone load zone_file='cn' zone_name='cn'")
self.no_assert_raise("zone load zone_file='cn', zone_name='cn', ")
......@@ -178,27 +178,27 @@ class TestCmdSyntax(unittest.TestCase):
self.no_assert_raise("zone set allow_update='1.1.1.1' zone_name='cn'")
self.no_assert_raise("zone set zone_name='cn'")
self.my_assert_raise(isc.cc.data.DataTypeError, "zone set zone_name ='cn', port='cn'")
self.no_assert_raise("zone reload_all")
self.no_assert_raise("zone reload_all")
def testCmdUnknownModuleSyntaxError(self):
self.my_assert_raise(CmdUnknownModuleSyntaxError, "zoned d")
self.my_assert_raise(CmdUnknownModuleSyntaxError, "dd dd ")
def testCmdUnknownCmdSyntaxError(self):
self.my_assert_raise(CmdUnknownCmdSyntaxError, "zone dd")
def testCmdMissParamSyntaxError(self):
self.my_assert_raise(CmdMissParamSyntaxError, "zone load zone_file='cn'")
self.my_assert_raise(CmdMissParamSyntaxError, "zone load zone_name='cn'")
self.my_assert_raise(CmdMissParamSyntaxError, "zone set allow_update='1.1.1.1'")
self.my_assert_raise(CmdMissParamSyntaxError, "zone set ")
def testCmdUnknownParamSyntaxError(self):
self.my_assert_raise(CmdUnknownParamSyntaxError, "zone load zone_d='cn'")
self.my_assert_raise(CmdUnknownParamSyntaxError, "zone reload_all zone_name = 'cn'")
self.my_assert_raise(CmdUnknownParamSyntaxError, "zone reload_all zone_name = 'cn'")
class TestModuleInfo(unittest.TestCase):
def test_get_param_name_by_position(self):
......@@ -212,36 +212,36 @@ class TestModuleInfo(unittest.TestCase):
self.assertEqual('sex', cmd.get_param_name_by_position(2, 3))
self.assertEqual('data', cmd.get_param_name_by_position(2, 4))
self.assertEqual('data', cmd.get_param_name_by_position(2, 4))
self.assertRaises(KeyError, cmd.get_param_name_by_position, 4, 4)
class TestNameSequence(unittest.TestCase):
"""
Test if the module/command/parameters is saved in the order creation
"""
def _create_bindcmd(self):
"""Create one bindcmd"""
"""Create one bindcmd"""
self._cmd = CommandInfo(name = "load")
self.module = ModuleInfo(name = "zone")
self.tool = bindcmd.BindCmdInterpreter()
self.tool = bindcmd.BindCmdInterpreter()
for random_str in self.random_names:
self._cmd.add_param(ParamInfo(name = random_str))
self.module.add_command(CommandInfo(name = random_str))
self.tool.add_module_info(ModuleInfo(name = random_str))
self.tool.add_module_info(ModuleInfo(name = random_str))
def setUp(self):
self.random_names = ['1erdfeDDWsd', '3fe', '2009erd', 'Fe231', 'tere142', 'rei8WD']
self._create_bindcmd()
def testSequence(self):
def testSequence(self):
param_names = self._cmd.get_param_names()
cmd_names = self.module.get_command_names()
module_names = self.tool.get_module_names()
i = 0
while i < len(self.random_names):
assert self.random_names[i] == param_names[i+1]
......@@ -342,7 +342,7 @@ class TestConfigCommands(unittest.TestCase):
# validate log message for socket.err
socket_err_output = io.StringIO()
sys.stdout = socket_err_output
self.assertRaises(None, self.tool.run())
self.assertEqual(1, self.tool.run())
self.assertEqual("Failed to send request, the connection is closed\n",
socket_err_output.getvalue())
socket_err_output.close()
......@@ -350,7 +350,7 @@ class TestConfigCommands(unittest.TestCase):
# validate log message for http.client.CannotSendRequest
cannot_send_output = io.StringIO()
sys.stdout = cannot_send_output
self.assertRaises(None, self.tool.run())
self.assertEqual(1, self.tool.run())
self.assertEqual("Can not send request, the connection is busy\n",
cannot_send_output.getvalue())
cannot_send_output.close()
......@@ -472,4 +472,4 @@ class TestCommandLineOptions(unittest.TestCase):
if __name__== "__main__":
unittest.main()