Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ISC Open Source Projects
Kea
Commits
5514dd78
Commit
5514dd78
authored
Mar 15, 2011
by
Michal 'vorner' Vaner
Browse files
Merge branch 'work/configuration'
parents
cd620bf3
7a3dc628
Changes
15
Hide whitespace changes
Inline
Side-by-side
configure.ac
View file @
5514dd78
...
...
@@ -663,6 +663,7 @@ AC_CONFIG_FILES([Makefile
src/lib/python/isc/net/tests/Makefile
src/lib/python/isc/notify/Makefile
src/lib/python/isc/notify/tests/Makefile
src/lib/python/isc/testutils/Makefile
src/lib/config/Makefile
src/lib/config/tests/Makefile
src/lib/config/tests/testdata/Makefile
...
...
src/bin/bind10/bind10.py.in
View file @
5514dd78
...
...
@@ -194,14 +194,21 @@ class CChannelConnectError(Exception): pass
class BoB:
"""Boss of BIND class."""
def __init__(self, msgq_socket_file=None, nocache=False, verbose=False,
setuid=None, username=None):
def __init__(self, msgq_socket_file=None, data_path=None,
config_filename=None, nocache=False, verbose=False, setuid=None,
username=None, cmdctl_port=None):
"""
Initialize the Boss of BIND. This is a singleton (only one can run).
The msgq_socket_file specifies the UNIX domain socket file that the
msgq process listens on. If verbose is True, then the boss reports
what it is doing.
Data path and config filename are passed trough to config manager
(if provided) and specify the config file to be used.
The cmdctl_port is passed to cmdctl and specify on which port it
should listen.
"""
self.cc_session = None
self.ccs = None
...
...
@@ -219,6 +226,9 @@ class BoB:
self.uid = setuid
self.username = username
self.verbose = verbose
self.data_path = data_path
self.config_filename = config_filename
self.cmdctl_port = cmdctl_port
def config_handler(self, new_config):
# If this is initial update, don't do anything now, leave it to startup
...
...
@@ -390,7 +400,12 @@ class BoB:
Starts the configuration manager process
"""
self.log_starting("b10-cfgmgr")
bind_cfgd = ProcessInfo("b10-cfgmgr", ["b10-cfgmgr"],
args = ["b10-cfgmgr"]
if self.data_path is not None:
args.append("--data-path=" + self.data_path)
if self.config_filename is not None:
args.append("--config-filename=" + self.config_filename)
bind_cfgd = ProcessInfo("b10-cfgmgr", args,
c_channel_env, uid=self.uid,
username=self.username)
self.processes[bind_cfgd.pid] = bind_cfgd
...
...
@@ -500,8 +515,13 @@ class BoB:
self.start_simple("b10-stats", c_channel_env)
def start_cmdctl(self, c_channel_env):
# XXX: we hardcode port 8080
self.start_simple("b10-cmdctl", c_channel_env, 8080)
"""
Starts the command control process
"""
args = ["b10-cmdctl"]
if self.cmdctl_port is not None:
args.append("--port=" + str(self.cmdctl_port))
self.start_process("b10-cmdctl", args, c_channel_env, self.cmdctl_port)
def start_all_processes(self):
"""
...
...
@@ -785,6 +805,50 @@ def process_rename(option, opt_str, value, parser):
"""Function that renames the process if it is requested by a option."""
isc.util.process.rename(value)
def parse_args(args=sys.argv[1:], Parser=OptionParser):
"""
Function for parsing command line arguments. Returns the
options object from OptionParser.
"""
parser = Parser(version=VERSION)
parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
type="string", default=None,
help="UNIX domain socket file the b10-msgq daemon will use")
parser.add_option("-n", "--no-cache", action="store_true", dest="nocache",
default=False, help="disable hot-spot cache in authoritative DNS server")
parser.add_option("-u", "--user", dest="user", type="string", default=None,
help="Change user after startup (must run as root)")
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="display more about what is going on")
parser.add_option("--pretty-name", type="string", action="callback",
callback=process_rename,
help="Set the process name (displayed in ps, top, ...)")
parser.add_option("-c", "--config-file", action="store",
dest="config_file", default=None,
help="Configuration database filename")
parser.add_option("-p", "--data-path", dest="data_path",
help="Directory to search for configuration files",
default=None)
parser.add_option("--cmdctl-port", dest="cmdctl_port", type="int",
default=None, help="Port of command control")
parser.add_option("--pid-file", dest="pid_file", type="string",
default=None,
help="file to dump the PID of the BIND 10 process")
(options, args) = parser.parse_args(args)
if options.cmdctl_port is not None:
try:
isc.net.parse.port_parse(options.cmdctl_port)
except ValueError as e:
parser.error(e)
if args:
parser.print_help()
sys.exit(1)
return options
def dump_pid(pid_file):
"""
Dump the PID of the current process to the specified file. If the given
...
...
@@ -814,33 +878,14 @@ def unlink_pid_file(pid_file):
if error.errno is not errno.ENOENT:
raise
def main():
global options
global boss_of_bind
# Enforce line buffering on stdout, even when not a TTY
sys.stdout = io.TextIOWrapper(sys.stdout.detach(), line_buffering=True)
# Parse any command-line options.
parser = OptionParser(version=VERSION)
parser.add_option("-m", "--msgq-socket-file", dest="msgq_socket_file",
type="string", default=None,
help="UNIX domain socket file the b10-msgq daemon will use")
parser.add_option("-n", "--no-cache", action="store_true", dest="nocache",
default=False, help="disable hot-spot cache in authoritative DNS server")
parser.add_option("-u", "--user", dest="user", type="string", default=None,
help="Change user after startup (must run as root)")
parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
help="display more about what is going on")
parser.add_option("--pretty-name", type="string", action="callback",
callback=process_rename,
help="Set the process name (displayed in ps, top, ...)")
parser.add_option("--pid-file", dest="pid_file", type="string",
default=None,
help="file to dump the PID of the BIND 10 process")
(options, args) = parser.parse_args()
if args:
parser.print_help()
sys.exit(1)
options = parse_args()
# Check user ID.
setuid = None
...
...
@@ -890,8 +935,9 @@ def main():
signal.signal(signal.SIGPIPE, signal.SIG_IGN)
# Go bob!
boss_of_bind = BoB(options.msgq_socket_file, options.nocache,
options.verbose, setuid, username)
boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
options.config_file, options.nocache, options.verbose,
setuid, username, options.cmdctl_port)
startup_result = boss_of_bind.startup()
if startup_result:
sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)
...
...
src/bin/bind10/bind10.xml
View file @
5514dd78
...
...
@@ -48,6 +48,8 @@
<arg><option>
-n
</option></arg>
<arg><option>
-u
<replaceable>
user
</replaceable></option></arg>
<arg><option>
-v
</option></arg>
<arg><option>
-c
<replaceable>
config-filename
</replaceable></option></arg>
<arg><option>
-p
<replaceable>
data_path
</replaceable></option></arg>
<arg><option>
--msgq-socket-file
<replaceable>
file
</replaceable></option></arg>
<arg><option>
--no-cache
</option></arg>
<arg><option>
--user
<replaceable>
user
</replaceable></option></arg>
...
...
@@ -80,6 +82,31 @@
<para>
The arguments are as follows:
</para>
<variablelist>
<varlistentry>
<term>
<option>
-c
</option><replaceable>
config-filename
</replaceable>
,
<option>
--config-file
</option>
<replaceable>
config-filename
</replaceable>
</term>
<listitem>
<para>
The configuration filename to use. Can be either absolute or
relative to data path. In case it is absolute, value of data path is
not considered.
</para>
<para>
Defaults to b10-config.db.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>
-p
</option><replaceable>
data-path
</replaceable>
,
<option>
--data-path
</option>
<replaceable>
data-path
</replaceable>
</term>
<listitem>
<para>
The path where BIND 10 programs look for various data files.
Currently only b10-cfgmgr uses it to locate the configuration file,
but the usage might be extended for other programs and other types
of files.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>
-m
</option>
<replaceable>
file
</replaceable>
,
...
...
src/bin/bind10/tests/bind10_test.py.in
View file @
5514dd78
from bind10 import ProcessInfo, BoB, dump_pid, unlink_pid_file
from bind10 import ProcessInfo, BoB,
parse_args,
dump_pid, unlink_pid_file
# XXX: environment tests are currently disabled, due to the preprocessor
# setup that we have now complicating the environment
...
...
@@ -9,6 +9,7 @@ import os
import signal
import socket
from isc.net.addr import IPAddr
from isc.testutils.parse_args import TestOptParser, OptsError
class TestProcessInfo(unittest.TestCase):
def setUp(self):
...
...
@@ -412,6 +413,57 @@ class TestStartStopProcessesBob(unittest.TestCase):
bob.config_handler({'start_auth': True, 'start_resolver': True})
class TestParseArgs(unittest.TestCase):
"""
This tests parsing of arguments of the bind10 master process.
"""
#TODO: Write tests for the original parsing, bad options, etc.
def test_no_opts(self):
"""
Test correct default values when no options are passed.
"""
options = parse_args([], TestOptParser)
self.assertEqual(None, options.data_path)
self.assertEqual(None, options.config_file)
self.assertEqual(None, options.cmdctl_port)
def test_data_path(self):
"""
Test it can parse the data path.
"""
self.assertRaises(OptsError, parse_args, ['-p'], TestOptParser)
self.assertRaises(OptsError, parse_args, ['--data-path'],
TestOptParser)
options = parse_args(['-p', '/data/path'], TestOptParser)
self.assertEqual('/data/path', options.data_path)
options = parse_args(['--data-path=/data/path'], TestOptParser)
self.assertEqual('/data/path', options.data_path)
def test_config_filename(self):
"""
Test it can parse the config switch.
"""
self.assertRaises(OptsError, parse_args, ['-c'], TestOptParser)
self.assertRaises(OptsError, parse_args, ['--config-file'],
TestOptParser)
options = parse_args(['-c', 'config-file'], TestOptParser)
self.assertEqual('config-file', options.config_file)
options = parse_args(['--config-file=config-file'], TestOptParser)
self.assertEqual('config-file', options.config_file)
def test_cmdctl_port(self):
"""
Test it can parse the command control port.
"""
self.assertRaises(OptsError, parse_args, ['--cmdctl-port=abc'],
TestOptParser)
self.assertRaises(OptsError, parse_args, ['--cmdctl-port=100000000'],
TestOptParser)
self.assertRaises(OptsError, parse_args, ['--cmdctl-port'],
TestOptParser)
options = parse_args(['--cmdctl-port=1234'], TestOptParser)
self.assertEqual(1234, options.cmdctl_port)
class TestPIDFile(unittest.TestCase):
def setUp(self):
self.pid_file = '@builddir@' + os.sep + 'bind10.pid'
...
...
src/bin/bindctl/tests/bindctl_test.py
View file @
5514dd78
...
...
@@ -26,6 +26,7 @@ import getpass
from
optparse
import
OptionParser
from
isc.config.config_data
import
ConfigData
,
MultiConfigData
from
isc.config.module_spec
import
ModuleSpec
from
isc.testutils.parse_args
import
TestOptParser
,
OptsError
from
bindctl_main
import
set_bindctl_options
from
bindctl
import
cmdparse
from
bindctl
import
bindcmd
...
...
@@ -452,23 +453,8 @@ class TestBindCmdInterpreter(unittest.TestCase):
class
TestCommandLineOptions
(
unittest
.
TestCase
):
class
FakeParserError
(
Exception
):
"""An exception thrown from FakeOptionParser on parser error.
"""
pass
class
FakeOptionParser
(
OptionParser
):
"""This fake class emulates the OptionParser class with customized
error handling for the convenient of tests.
"""
def
__init__
(
self
):
OptionParser
.
__init__
(
self
)
def
error
(
self
,
msg
):
raise
TestCommandLineOptions
.
FakeParserError
def
setUp
(
self
):
self
.
parser
=
self
.
FakeOption
Parser
()
self
.
parser
=
TestOpt
Parser
()
set_bindctl_options
(
self
.
parser
)
def
test_csv_file_dir
(
self
):
...
...
@@ -481,7 +467,7 @@ class TestCommandLineOptions(unittest.TestCase):
self
.
assertEqual
(
'some_dir'
,
options
.
csv_file_dir
)
# missing option arg; should trigger parser error.
self
.
assertRaises
(
self
.
FakeParser
Error
,
self
.
parser
.
parse_args
,
self
.
assertRaises
(
Opts
Error
,
self
.
parser
.
parse_args
,
[
'--csv-file-dir'
])
if
__name__
==
"__main__"
:
...
...
src/bin/cfgmgr/b10-cfgmgr.py.in
View file @
5514dd78
...
...
@@ -22,6 +22,7 @@ from isc.cc import SessionError
import isc.util.process
import signal
import os
from optparse import OptionParser
isc.util.process.rename()
...
...
@@ -41,18 +42,34 @@ if "B10_FROM_SOURCE" in os.environ:
else:
PREFIX = "@prefix@"
DATA_PATH = "@localstatedir@/@PACKAGE@".replace("${prefix}", PREFIX)
DEFAULT_CONFIG_FILE = "b10-config.db"
cm = None
def parse_options(args=sys.argv[1:], Parser=OptionParser):
parser = Parser()
parser.add_option("-p", "--data-path", dest="data_path",
help="Directory to search for configuration files " +
"(default=" + DATA_PATH + ")", default=DATA_PATH)
parser.add_option("-c", "--config-filename", dest="config_file",
help="Configuration database filename " +
"(default=" + DEFAULT_CONFIG_FILE + ")",
default=DEFAULT_CONFIG_FILE)
(options, args) = parser.parse_args(args)
if args:
parser.error("No non-option arguments allowed")
return options
def signal_handler(signal, frame):
global cm
if cm:
cm.running = False
def main():
options = parse_options()
global cm
try:
cm = ConfigManager(
DATA_PATH
)
cm = ConfigManager(
options.data_path, options.config_file
)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
cm.read_config()
...
...
src/bin/cfgmgr/b10-cfgmgr.xml
View file @
5514dd78
...
...
@@ -41,16 +41,13 @@
</copyright>
</docinfo>
<!--
<refsynopsisdiv>
<cmdsynopsis>
<command></command>
<arg><option></option></arg>
<arg choice="opt"></arg>
<arg choice="opt"></arg>
<command>
b10-cfgmgr
</command>
<arg><option>
-c
<replaceable>
config-filename
</replaceable></option></arg>
<arg><option>
-p
<replaceable>
data_path
</replaceable></option></arg>
</cmdsynopsis>
</refsynopsisdiv>
-->
<refsect1>
<title>
DESCRIPTION
</title>
...
...
@@ -93,24 +90,38 @@
</para>
</refsect1>
<!--
<refsect1>
<title>
ARGUMENTS
</title>
<para>
<orderedlist numeration="loweralpha">
<para>
The arguments are as follows:
</para>
<variablelist>
<varlistentry>
<term>
<option>
-c
</option><replaceable>
config-filename
</replaceable>
,
<option>
--config-filename
</option>
<replaceable>
config-filename
</replaceable>
</term>
<listitem>
<para>
</para>
<para>
The configuration database filename to use. Can be either
absolute or relative to data path.
</para>
<para>
Defaults to b10-config.db
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<option>
-p
</option><replaceable>
data-path
</replaceable>
,
<option>
--data-path
</option>
<replaceable>
data-path
</replaceable>
</term>
<listitem>
<para>
</para>
<para>
The path where BIND 10 looks for files. The
configuration file is looked for here, if it is relative. If it is
absolute, the path is ignored.
</para>
</listitem>
</orderedlist>
</para>
</varlistentry>
</variablelist>
</refsect1>
-->
<refsect1>
<title>
FILES
</title>
<!-- TODO: fix path -->
...
...
src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
View file @
5514dd78
...
...
@@ -20,9 +20,10 @@
import unittest
import os
import sys
from isc.testutils.parse_args import OptsError, TestOptParser
class MyConfigManager:
def __init__(self, path):
def __init__(self, path
, filename
):
self._path = path
self.read_config_called = False
self.notify_boss_called = False
...
...
@@ -88,6 +89,69 @@ class TestConfigManagerStartup(unittest.TestCase):
sys.modules.pop("b10-cfgmgr")
class TestParseArgs(unittest.TestCase):
"""
Test for the parsing of command line arguments. We provide a different
array to parse instead.
"""
def test_defaults(self):
"""
Test the default values when no options are provided.
"""
# Pass it empty array, not our arguments
b = __import__("b10-cfgmgr")
parsed = b.parse_options([], TestOptParser)
self.assertEqual(b.DATA_PATH, parsed.data_path)
self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
def test_wrong_args(self):
"""
Test it fails when we pass invalid option.
"""
b = __import__("b10-cfgmgr")
self.assertRaises(OptsError, b.parse_options, ['--wrong-option'],
TestOptParser)
def test_not_arg(self):
"""
Test it fails when there's an argument that's not option
(eg. without -- at the beginning).
"""
b = __import__("b10-cfgmgr")
self.assertRaises(OptsError, b.parse_options, ['not-option'],
TestOptParser)
def test_datapath(self):
"""
Test overwriting the data path.
"""
b = __import__("b10-cfgmgr")
parsed = b.parse_options(['--data-path=/path'], TestOptParser)
self.assertEqual('/path', parsed.data_path)
self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
parsed = b.parse_options(['-p', '/path'], TestOptParser)
self.assertEqual('/path', parsed.data_path)
self.assertEqual(b.DEFAULT_CONFIG_FILE, parsed.config_file)
self.assertRaises(OptsError, b.parse_options, ['-p'], TestOptParser)
self.assertRaises(OptsError, b.parse_options, ['--data-path'],
TestOptParser)
def test_db_filename(self):
"""
Test setting the configuration database file.
"""
b = __import__("b10-cfgmgr")
parsed = b.parse_options(['--config-filename=filename'],
TestOptParser)
self.assertEqual(b.DATA_PATH, parsed.data_path)
self.assertEqual("filename", parsed.config_file)
parsed = b.parse_options(['-c', 'filename'], TestOptParser)
self.assertEqual(b.DATA_PATH, parsed.data_path)
self.assertEqual("filename", parsed.config_file)
self.assertRaises(OptsError, b.parse_options, ['-c'], TestOptParser)
self.assertRaises(OptsError, b.parse_options, ['--config-filename'],
TestOptParser)
if __name__ == '__main__':
unittest.main()
...
...
src/lib/python/isc/Makefile.am
View file @
5514dd78
SUBDIRS
=
datasrc cc config log net notify util
SUBDIRS
=
datasrc cc config log net notify util
testutils
python_PYTHON
=
__init__.py
...
...
src/lib/python/isc/config/cfgmgr.py
View file @
5514dd78
...
...
@@ -44,25 +44,36 @@ class ConfigManagerData:
"""This class hold the actual configuration information, and
reads it from and writes it to persistent storage"""
def
__init__
(
self
,
data_path
,
file_name
=
"b10-config.db"
):
def
__init__
(
self
,
data_path
,
file_name
):
"""Initialize the data for the configuration manager, and
set the version and path for the data store. Initializing
this does not yet read the database, a call to
read_from_file is needed for that."""
read_from_file is needed for that.
In case the file_name is absolute, data_path is ignored
and the directory where the file_name lives is used instead.
"""
self
.
data
=
{}
self
.
data
[
'version'
]
=
config_data
.
BIND10_CONFIG_DATA_VERSION
self
.
data_path
=
data_path
self
.
db_filename
=
data_path
+
os
.
sep
+
file_name
if
os
.
path
.
isabs
(
file_name
):
self
.
db_filename
=
file_name
self
.
data_path
=
os
.
path
.
dirname
(
file_name
)
else
:
self
.
db_filename
=
data_path
+
os
.
sep
+
file_name
self
.
data_path
=
data_path
def
read_from_file
(
data_path
,
file_name
):
"""Read the current configuration found in the file file_name.
If file_name is absolute, data_path is ignored. Otherwise
we look for the file_name in data_path directory.
def
read_from_file
(
data_path
,
file_name
=
"b10-config.db"
):
"""Read the current configuration found in the file at
data_path. If the file does not exist, a
ConfigManagerDataEmpty exception is raised. If there is a
parse error, or if the data in the file has the wrong
version, a ConfigManagerDataReadError is raised. In the first
case, it is probably safe to log and ignore. In the case of
the second exception, the best way is probably to report the
error and stop loading the system."""
If the file does not exist, a ConfigManagerDataEmpty exception is
raised. If there is a parse error, or if the data in the file has
the wrong version, a ConfigManagerDataReadError is raised. In the
first case, it is probably safe to log and ignore. In the case of
the second exception, the best way is probably to report the error
and stop loading the system.
"""
config
=
ConfigManagerData
(
data_path
,
file_name
)
file
=
None
try
:
...
...
@@ -142,20 +153,24 @@ class ConfigManagerData:
class
ConfigManager
:
"""Creates a configuration manager. The data_path is the path
to the directory containing the b10-config.db file.
to the directory containing the configuraton file,
database_filename points to the configuration file.
If session is set, this will be used as the communication
channel session. If not, a new session will be created.
The ability to specify a custom session is for testing purposes
and should not be needed for normal usage."""
def
__init__
(
self
,
data_path
,
session
=
None
):
def
__init__
(
self
,
data_path
,
database_filename
,
session
=
None
):
"""Initialize the configuration manager. The data_path string
is the path to the directory where the configuration is
stored (in <data_path>/b10-config.db). Session is an optional
stored (in <data_path>/<database_filename> or in
<database_filename>, if it is absolute). The dabase_filename
is the config file to load. Session is an optional
cc-channel session. If this is not given, a new one is
created"""
created
.
"""
self
.
data_path
=
data_path
self
.
database_filename
=
database_filename
self
.
module_specs
=
{}
self
.
config
=
ConfigManagerData
(
data_path
)
self
.
config
=
ConfigManagerData
(
data_path
,
database_filename
)
if
session
:
self
.
cc
=
session
else
:
...
...
@@ -223,17 +238,18 @@ class ConfigManager:
return
commands
def
read_config
(
self
):
"""Read the current configuration from the b10-config.db file
at the path specificied at init()"""
"""Read the current configuration from the file specificied at init()"""
try
:
self
.
config
=
ConfigManagerData
.
read_from_file
(
self
.
data_path
)
self
.
config
=
ConfigManagerData
.
read_from_file
(
self
.
data_path
,
self
.
\
database_filename
)
except
ConfigManagerDataEmpty
:
# ok, just start with an empty config
self
.
config
=
ConfigManagerData
(
self
.
data_path
)
self
.
config
=
ConfigManagerData
(
self
.
data_path
,
self
.
database_filename
)
def
write_config
(
self
):
"""Write the current configuration to the b10-config.db file
at the path specificied at init()"""
"""Write the current configuration to the file specificied at init()"""
self
.
config
.
write_to_file
()
def
_handle_get_module_spec
(
self
,
cmd
):
...
...
src/lib/python/isc/config/tests/cfgmgr_test.py
View file @
5514dd78
...
...
@@ -27,9 +27,20 @@ class TestConfigManagerData(unittest.TestCase):
def
setUp
(
self
):
self
.
data_path
=
os
.
environ
[
'CONFIG_TESTDATA_PATH'
]