Commit 8523458e authored by Likun Zhang's avatar Likun Zhang
Browse files

1. Add support to cmdctl.spec file, now there are three configurable items for cmdctl: 'key_file', 'cert_file' and 'accounts_file', all of them can be changed in 
runtime.  Also there is two command spported by cmdctl: shutdown and print_settings .
2. Refator some code of cmdctl and add corresponding test cases.
3. Rename module name from 'Cmd-Ctrld' to 'Cmdctl'.

git-svn-id: svn://bind10.isc.org/svn/bind10/branches/trac127@2253 e5f2f494-b856-4b98-b285-d166d9295462
parent 8e42f074
......@@ -428,6 +428,7 @@ AC_OUTPUT([src/bin/cfgmgr/b10-cfgmgr.py
src/bin/cmdctl/cmdctl.py
src/bin/cmdctl/run_b10-cmdctl.sh
src/bin/cmdctl/tests/cmdctl_test
src/bin/cmdctl/cmdctl.spec.pre
src/bin/xfrin/tests/xfrin_test
src/bin/xfrin/xfrin.py
src/bin/xfrin/xfrin.spec.pre
......
......@@ -353,7 +353,7 @@ class BoB:
def stop_all_processes(self):
"""Stop all processes."""
cmd = { "command": ['shutdown']}
self.cc_session.group_sendmsg(cmd, 'Boss', 'Cmd-Ctrld')
self.cc_session.group_sendmsg(cmd, 'Boss', 'Cmdctl')
self.cc_session.group_sendmsg(cmd, "Boss", "ConfigManager")
self.cc_session.group_sendmsg(cmd, "Boss", "Auth")
self.cc_session.group_sendmsg(cmd, "Boss", "Xfrout")
......
......@@ -19,7 +19,7 @@ b10_cmdctl_DATA += cmdctl.spec
EXTRA_DIST = $(CMDCTL_CONFIGURATIONS)
EXTRA_DIST += cmdctl.spec
CLEANFILES= b10-cmdctl cmdctl.pyc
CLEANFILES= b10-cmdctl cmdctl.pyc cmdctl.spec
man_MANS = b10-cmdctl.8
EXTRA_DIST += $(man_MANS) b10-cmdctl.xml
......@@ -31,6 +31,9 @@ b10-cmdctl.8: b10-cmdctl.xml
endif
cmdctl.spec: cmdctl.spec.pre
$(SED) -e "s|@@SYSCONFDIR@@|$(sysconfdir)|" cmdctl.spec.pre >$@
# TODO: does this need $$(DESTDIR) also?
# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
b10-cmdctl: cmdctl.py
......
. Refine code for b10-cmdctl.
. Add value type check according module specification.
. Add return code for RESTful API document of b10-cmdctl.
. Add more unit tests for b10-cmdctl.
. Update man page for b10-cmdctl?
. Add check for the content of key/certificate file
(when cmdctl starts or is configured by bindctl).
. Add id to each command, so the receiver knows if the response is what it wants.
. Make cmdctl can be configured through bindctl.(after id is added)
This diff is collapsed.
......@@ -7,32 +7,22 @@
"item_name": "key_file",
"item_type": "string",
"item_optional": False,
"item_default": 'cmdctl-keyfile.pem'
"item_default": '@@SYSCONFDIR@@/@PACKAGE@/cmdctl-keyfile.pem'
},
{
"item_name": "cert_file",
"item_type": "string",
"item_optional": False,
"item_default": 'cmdctl-certfile.pem'
"item_default": '@@SYSCONFDIR@@/@PACKAGE@/cmdctl-certfile.pem'
},
{
"item_name": "accounts_file",
"item_type": "string",
"item_optional": False,
"item_default": 'cmdctl-accounts.csv'
"item_default": '@@SYSCONFDIR@@/@PACKAGE@/cmdctl-accounts.csv'
}
],
"commands": [
{
"command_name": "print_message",
"command_description": "Print the given message to stdout",
"command_args": [ {
"item_name": "message",
"item_type": "string",
"item_optional": False,
"item_default": ""
} ]
},
{
"command_name": "print_settings",
"command_description": "Print some_string and some_int to stdout",
......@@ -40,9 +30,9 @@
},
{
"command_name": "shutdown",
"command_description": "Shut down cmdctl",
"command_description": "shutdown cmdctl",
"command_args": []
}
},
]
}
}
......
......@@ -16,6 +16,7 @@
import unittest
import socket
import tempfile
from cmdctl import *
# Rewrite the class for unittest.
......@@ -42,17 +43,20 @@ class MySecureHTTPRequestHandler(SecureHTTPRequestHandler):
os.remove('tmp.file')
class MySecureHTTPServer(SecureHTTPServer):
class FakeSecureHTTPServer(SecureHTTPServer):
def __init__(self):
self.user_sessions = {}
self.cmdctl = FakeCommandControlForTestRequestHandler()
self._verbose = True
self._user_infos = {}
self.idle_timeout = 1200
self.cmdctrl = MyCommandControl()
self._verbose = False
self._lock = threading.Lock()
class MyCommandControl(CommandControl):
class FakeCommandControlForTestRequestHandler(CommandControl):
def __init__(self):
self.config_data = {}
self.module_spec = {}
self._config_data = {}
self.modules_spec = {}
self._lock = threading.Lock()
def send_command(self, mod, cmd, param):
return 0, {}
......@@ -60,14 +64,17 @@ class MyCommandControl(CommandControl):
class TestSecureHTTPRequestHandler(unittest.TestCase):
def setUp(self):
self.old_stdout = sys.stdout
sys.stdout = open(os.devnull, 'w')
self.handler = MySecureHTTPRequestHandler()
self.handler.server = MySecureHTTPServer()
self.handler.server = FakeSecureHTTPServer()
self.handler.server.user_sessions = {}
self.handler.server.user_infos = {}
self.handler.server._user_infos = {}
self.handler.headers = {}
self.handler.rfile = open("check.tmp", 'w+b')
def tearDown(self):
sys.stdout = self.old_stdout
self.handler.rfile.close()
os.remove('check.tmp')
......@@ -154,7 +161,7 @@ class TestSecureHTTPRequestHandler(unittest.TestCase):
self.handler.headers['Content-Length'] = len
self.handler.rfile.seek(0, 0)
self.handler.server.user_infos['root'] = ['aa', 'aaa']
self.handler.server._user_infos['root'] = ['aa', 'aaa']
ret, msg = self.handler._check_user_name_and_pwd()
self.assertTrue(ret == False)
self.assertEqual(msg, ['password doesn\'t match'])
......@@ -185,7 +192,7 @@ class TestSecureHTTPRequestHandler(unittest.TestCase):
self.handler.headers['Content-Length'] = len
self.handler.rfile.seek(0, 0)
self.handler.server.user_infos['root'] = ['aa', 'aaa']
self.handler.server._user_infos['root'] = ['aa', 'aaa']
ret, msg = self.handler._check_user_name_and_pwd()
self.assertTrue(ret == False)
self.assertEqual(msg, ['need password'])
......@@ -247,8 +254,8 @@ class TestSecureHTTPRequestHandler(unittest.TestCase):
self.handler.rfile.seek(0, 0)
self.handler.path = '/module/command'
self.handler.server.cmdctrl.module_spec = {}
self.handler.server.cmdctrl.module_spec['module'] = self._gen_module_spec()
self.handler.server.cmdctl.modules_spec = {}
self.handler.server.cmdctl.modules_spec['module'] = self._gen_module_spec()
rcode, reply = self.handler._handle_post_request()
self.assertEqual(http.client.OK, rcode)
......@@ -259,10 +266,150 @@ class TestSecureHTTPRequestHandler(unittest.TestCase):
self.handler.rfile.seek(0, 0)
self.handler.path = '/module/command'
self.handler.server.cmdctrl.module_spec = {}
self.handler.server.cmdctrl.module_spec['module'] = self._gen_module_spec()
self.handler.server.cmdctl.modules_spec = {}
self.handler.server.cmdctl.modules_spec['module'] = self._gen_module_spec()
rcode, reply = self.handler._handle_post_request()
self.assertEqual(http.client.BAD_REQUEST, rcode)
class MyCommandControl(CommandControl):
def _get_modules_specification(self):
return {}
def _get_config_data_from_config_manager(self):
return {}
def _setup_session(self):
module_spec = isc.config.module_spec_from_file(SPECFILE_LOCATION)
config = isc.config.config_data.ConfigData(module_spec)
self._cmdctl_config_data = config.get_full_config()
def _handle_msg_from_msgq(self):
pass
class TestCommandControl(unittest.TestCase):
def setUp(self):
self.old_stdout = sys.stdout
sys.stdout = open(os.devnull, 'w')
self.cmdctl = MyCommandControl(None, True)
def tearDown(self):
sys.stdout = self.old_stdout
def _check_config(self, cmdctl):
key, cert, account = cmdctl.get_cmdctl_config_data()
self.assertIsNotNone(key)
self.assertIsNotNone(cert)
self.assertIsNotNone(account)
def test_get_cmdctl_config_data(self):
old_env = os.environ
if 'B10_FROM_SOURCE' in os.environ:
del os.environ['B10_FROM_SOURCE']
self.cmdctl.get_cmdctl_config_data()
self._check_config(self.cmdctl)
os.environ = old_env
old_env = os.environ
os.environ['B10_FROM_SOURCE'] = '../'
self._check_config(self.cmdctl)
os.environ = old_env
def test_parse_command_result(self):
self.assertEqual({}, self.cmdctl._parse_command_result(1, {'error' : 1}))
self.assertEqual({'a': 1}, self.cmdctl._parse_command_result(0, {'a' : 1}))
def _check_answer(self, answer, rcode_, msg_):
rcode, msg = ccsession.parse_answer(answer)
self.assertEqual(rcode, rcode_)
self.assertEqual(msg, msg_)
def test_command_handler(self):
answer = self.cmdctl.command_handler('unknown-command', None)
self._check_answer(answer, 1, 'unknown command: unknown-command')
answer = self.cmdctl.command_handler('print_settings', None)
self._check_answer(answer, 0, None)
def test_check_config_handler(self):
answer = self.cmdctl.config_handler({'non-exist': 123})
self._check_answer(answer, 1, 'unknown config item: non-exist')
old_env = os.environ
os.environ['B10_FROM_SOURCE'] = '../'
self._check_config(self.cmdctl)
answer = self.cmdctl.config_handler({'key_file' : self.cmdctl._cmdctl_config_data['key_file']})
self._check_answer(answer, 0, None)
answer = self.cmdctl.config_handler({'cert_file' : self.cmdctl._cmdctl_config_data['cert_file']})
self._check_answer(answer, 0, None)
answer = self.cmdctl.config_handler({'accounts_file' : self.cmdctl._cmdctl_config_data['accounts_file']})
self._check_answer(answer, 0, None)
os.environ = old_env
answer = self.cmdctl.config_handler({'key_file': '/user/non-exist_folder'})
self._check_answer(answer, 1, "the file doesn't exist: /user/non-exist_folder")
answer = self.cmdctl.config_handler({'cert_file': '/user/non-exist_folder'})
self._check_answer(answer, 1, "the file doesn't exist: /user/non-exist_folder")
answer = self.cmdctl.config_handler({'accounts_file': '/user/non-exist_folder'})
self._check_answer(answer, 1,
"Invalid accounts file: [Errno 2] No such file or directory: '/user/non-exist_folder'")
# Test with invalid accounts file
file_name = 'tmp.account.file'
temp_file = open(file_name, 'w')
writer = csv.writer(temp_file)
writer.writerow(['a', 'b'])
temp_file.close()
answer = self.cmdctl.config_handler({'accounts_file': file_name})
self._check_answer(answer, 1, "Invalid accounts file: list index out of range")
os.remove(file_name)
def test_send_command(self):
rcode, value = self.cmdctl.send_command(MODULE_NAME, 'print_settings', None)
self.assertEqual(rcode, 0)
class MySecureHTTPServer(SecureHTTPServer):
def server_bind(self):
pass
class TestSecureHTTPServer(unittest.TestCase):
def setUp(self):
self.old_stdout = sys.stdout
sys.stdout = open(os.devnull, 'w')
self.server = MySecureHTTPServer(('localhost', 8080),
MySecureHTTPRequestHandler,
MyCommandControl, verbose=True)
def tearDown(self):
sys.stdout = self.old_stdout
def test_create_user_info(self):
self.server._create_user_info('/local/not-exist')
self.assertEqual(0, len(self.server._user_infos))
self.server._create_user_info('../cmdctl-accounts.csv')
self.assertEqual(1, len(self.server._user_infos))
self.assertTrue('root' in self.server._user_infos)
def test_wrap_sock_in_ssl_context(self):
sock = socket.socket()
self.server._wrap_socket_in_ssl_context(sock,
'../cmdctl-keyfile',
'../cmdctl-certfile')
class TestFuncNotInClass(unittest.TestCase):
def test_check_port(self):
self.assertRaises(OptionValueError, check_port, None, 'port', -1, None)
self.assertRaises(OptionValueError, check_port, None, 'port', 65536, None)
self.assertRaises(OptionValueError, check_addr, None, 'ipstr', 'a.b.d', None)
self.assertRaises(OptionValueError, check_addr, None, 'ipstr', '1::0:a.b', None)
if __name__== "__main__":
unittest.main()
......@@ -347,7 +347,7 @@ class ConfigManager:
# passes both specification and commands at once
spec_update = ccsession.create_command(ccsession.COMMAND_MODULE_SPECIFICATION_UPDATE,
[ spec.get_module_name(), spec.get_full_spec() ])
self.cc.group_sendmsg(spec_update, "Cmd-Ctrld")
self.cc.group_sendmsg(spec_update, "Cmdctl")
return ccsession.create_answer(0)
def handle_msg(self, msg):
......
......@@ -271,9 +271,9 @@ class TestConfigManager(unittest.TestCase):
# the name here is actually wrong (and hardcoded), but needed in the current version
# TODO: fix that
#self.assertEqual({'specification_update': [ self.name, self.spec ] },
# self.fake_session.get_message("Cmd-Ctrld", None))
# self.fake_session.get_message("Cmdctl", None))
#self.assertEqual({'commands_update': [ self.name, self.commands ] },
# self.fake_session.get_message("Cmd-Ctrld", None))
# self.fake_session.get_message("Cmdctl", None))
self._handle_msg_helper({ "command":
["shutdown"]
......
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