src/bin/usermgr/run_b10-cmdctl-usermgr.sh
src/bin/usermgr/b10-cmdctl-usermgr.py
src/bin/memmgr/memmgr.py
+ src/bin/memmgr/memmgr.spec.pre
src/bin/msgq/msgq.py
src/bin/msgq/run_msgq.sh
src/bin/auth/auth.spec.pre
/b10-memmgr
/memmgr.py
+/memmgr.spec
/b10-memmgr.8
CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/memmgr_messages.py
CLEANFILES += $(PYTHON_LOGMSGPKG_DIR)/work/memmgr_messages.pyc
-EXTRA_DIST = memmgr_messages.mes memmgr.spec
+EXTRA_DIST = memmgr_messages.mes
man_MANS = b10-memmgr.8
DISTCLEANFILES = $(man_MANS)
$(top_builddir)/src/lib/log/compiler/message \
-d $(PYTHON_LOGMSGPKG_DIR)/work -p $(srcdir)/memmgr_messages.mes
+memmgr.spec: memmgr.spec.pre
+ $(SED) -e "s|@@LOCALSTATEDIR@@|$(localstatedir)|" memmgr.spec.pre > $@
+
# this is done here since configure.ac AC_OUTPUT doesn't expand exec_prefix
b10-memmgr: memmgr.py $(PYTHON_LOGMSGPKG_DIR)/work/memmgr_messages.py
$(SED) -e "s|@@PYTHONPATH@@|@pyexecdir@|" memmgr.py >$@
sys.path.append('@@PYTHONPATH@@')
import isc.log
+#from isc.log import DBGLVL_TRACE_BASIC
from isc.log_messages.memmgr_messages import *
from isc.server_common.bind10_server import BIND10Server
isc.log.init('b10-memmgr', buffer=True)
logger = isc.log.Logger(MODULE_NAME)
+class ConfigError(Exception):
+ """An exception class raised for configuration errors of Memmgr."""
+ pass
+
class Memmgr(BIND10Server):
+ def __init__(self):
+ # configurable parameter: initially this is the only param, so
+ # we only maintain as a single attribute. As the class is extended
+ # and more configurable, consider introducing a compound type or
+ # class.
+ # This is defined as "protected" so tests can inspect it; others
+ # shouldn't use it directly.
+ self._mapped_file_dir = None
+
def _config_handler(self, new_config):
+ """Configuration handler, called via BIND10Server.
+
+ This method must be exception free. We assume minimum validity
+ about the parameter, though: it should be a valid dict, and conform
+ to the type specification of the spec file.
+
+ """
+ logger.debug(logger.DBGLVL_TRACE_BASIC, MEMMGR_CONFIG_UPDATE)
+
+ # Default answer:
+ answer = isc.config.create_answer(0)
+
+ # If this is the first time, initialize the local attributes with the
+ # latest full config data, which consist of the defaults with
+ # possibly overridden by user config. Otherwise, just apply the latest
+ # diff.
+ if self._mapped_file_dir is None:
+ new_config = self.mod_ccsession.get_full_config()
+ try:
+ self.__update_config(new_config)
+ except Exception as ex:
+ logger.error(MEMMGR_CONFIG_FAIL, ex)
+ answer = isc.config.create_answer(
+ 1, 'Memmgr failed to apply configuration updates: ' + str(ex))
+
+ return answer
+
+ def __update_config(self, new_config):
+ """Apply config changes to local attributes.
+
+ This is a subroutine of _config_handler. It's supposed to provide
+ strong exception guarantee: either all changes successfully apply
+ or, if any error is found, none applies. In the latter case the
+ entire original configuration should be kept.
+
+ Errors are to be reported as an exception.
+
+ """
+ new_mapped_file_dir = new_config.get('mapped_file_dir')
+ if new_mapped_file_dir is not None:
+ if not os.path.isdir(new_mapped_file_dir):
+ raise ConfigError('mapped_file_dir is not a directory: ' +
+ new_mapped_file_dir)
+ if not os.access(new_mapped_file_dir, os.W_OK):
+ raise ConfigError('mapped_file_dir is not writable: ' +
+ new_mapped_file_dir)
+ self._mapped_file_dir = new_mapped_file_dir
+
+ def _setup_module(self):
+ """Module specific initialization for BIND10Server."""
pass
if '__main__' == __name__:
"module_spec": {
"module_name": "Memmgr",
"config_data": [
+ { "item_name": "mapped_file_dir",
+ "item_type": "string",
+ "item_optional": true,
+ "item_default": "@@LOCALSTATEDIR@@/@PACKAGE@/mapped_files"
+ }
],
"commands": [
{
# <topsrcdir>/tools/reorder_message_file.py to make sure the
# messages are in the correct order.
-% MEMMGR_CONFIG_UPDATE received new configuration: %1
+% MEMMGR_CONFIG_FAIL failed to apply configuration updates: %1
+The memmgr daemon tried to apply configuration updates but found an error.
+The cause of the error is included in the message. None of the received
+updates applied, and the daemon keeps running with the previous configuration.
+
+% MEMMGR_CONFIG_UPDATE received new configuration
+A debug message. The memmgr daemon receives configuratiopn updates
+and is now applying them to its running configurations.
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import unittest
+import os
+import re
import isc.log
import isc.config
+from isc.config import parse_answer
import memmgr
+from isc.testutils.ccsession_mock import MockModuleCCSession
+
+class MyCCSession(MockModuleCCSession, isc.config.ConfigData):
+ def __init__(self, specfile, config_handler, command_handler):
+ super().__init__()
+ specfile = os.environ['B10_FROM_BUILD'] + '/src/bin/memmgr/memmgr.spec'
+ module_spec = isc.config.module_spec_from_file(specfile)
+ isc.config.ConfigData.__init__(self, module_spec)
+
+ def start(self):
+ pass
class MockMemmgr(memmgr.Memmgr):
- pass
+ def _setup_ccsession(self):
+ orig_cls = isc.config.ModuleCCSession
+ isc.config.ModuleCCSession = MyCCSession
+ try:
+ super()._setup_ccsession()
+ except Exception:
+ raise
+ finally:
+ isc.config.ModuleCCSession = orig_cls
class TestMemmgr(unittest.TestCase):
def setUp(self):
+ # Some tests use this directory. Make sure it doesn't pre-exist.
+ self.__test_mapped_file_dir = \
+ os.environ['B10_FROM_BUILD'] + \
+ '/src/bin/memmgr/tests/test_mapped_files'
+ if os.path.isdir(self.__test_mapped_file_dir):
+ os.rmdir(self.__test_mapped_file_dir)
+
self.__mgr = MockMemmgr()
+ # Fake some 'os' module functions for easier tests
+ self.__orig_os_access = os.access
+ self.__orig_isdir = os.path.isdir
+
+ def tearDown(self):
+ # Restore faked values
+ os.access = self.__orig_os_access
+ os.path.isdir = self.__orig_isdir
+
+ # If at test created a mapped-files directory, delete it.
+ if os.path.isdir(self.__test_mapped_file_dir):
+ os.rmdir(self.__test_mapped_file_dir)
+
+ def test_init(self):
+ self.assertIsNone(self.__mgr._mapped_file_dir)
+
+ def test_configure(self):
+ self.__mgr._setup_ccsession()
+
+ # Pretend specified directories exist and writable
+ os.path.isdir = lambda x: True
+ os.access = lambda x, y: True
+
+ # At the initial configuration, if mapped_file_dir isn't specified,
+ # the default value will be set.
+ self.assertEqual((0, None),
+ parse_answer(self.__mgr._config_handler({})))
+ self.assertEqual('mapped_files',
+ self.__mgr._mapped_file_dir.split('/')[-1])
+
+ # Update the configuration.
+ user_cfg = {'mapped_file_dir': '/some/path/dir'}
+ self.assertEqual((0, None),
+ parse_answer(self.__mgr._config_handler(user_cfg)))
+ self.assertEqual('/some/path/dir', self.__mgr._mapped_file_dir)
+
+ # Bad update: diretory doesn't exist (we assume it really doesn't
+ # exist in the tested environment). Update won't be made.
+ os.path.isdir = self.__orig_isdir # use real library
+ user_cfg = {'mapped_file_dir': '/another/path/dir'}
+ answer = parse_answer(self.__mgr._config_handler(user_cfg))
+ self.assertEqual(1, answer[0])
+ self.assertIsNotNone(re.search('not a directory', answer[1]))
+
+ # Bad update: directory exists but is not readable.
+ os.mkdir(self.__test_mapped_file_dir, 0o500) # drop writable bit
+ os.access = self.__orig_os_access
+ user_cfg = {'mapped_file_dir': self.__test_mapped_file_dir}
+ answer = parse_answer(self.__mgr._config_handler(user_cfg))
+ self.assertEqual(1, answer[0])
+ self.assertIsNotNone(re.search('not writable', answer[1]))
if __name__== "__main__":
isc.log.resetUnitTestRootLogger()