BIND 10 master, updated. 423640029ecf6f138eb8b160fc40ca5afef44e4a [master] update changelog for merge of #1443

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Mar 27 09:17:01 UTC 2012


The branch, master has been updated
       via  423640029ecf6f138eb8b160fc40ca5afef44e4a (commit)
       via  52b36c921ee59ec69deefb6123cbdb1b91dc3bc7 (commit)
       via  90df5f971e7988a7a024ce95fc83dd9dea3c9b6f (commit)
       via  0161c02ad4d593dc6a7ad5b352cf7db4e70b3b48 (commit)
       via  d98c6ee0863c784e79204242a3d868d4aedc3d5a (commit)
       via  04e0ef7fe56b4c9c65b71bba010147af4506675d (commit)
       via  24b2851dc08da2e9d63a072c1c8b4550ef919996 (commit)
       via  e17fed2f2bf0061fe942cc8ae9c31f44f0d6e303 (commit)
      from  46cd22e159df5387c036e285e8398e9f1320e2b4 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 423640029ecf6f138eb8b160fc40ca5afef44e4a
Author: Jelte Jansen <jelte at isc.org>
Date:   Tue Mar 27 11:16:43 2012 +0200

    [master] update changelog for merge of #1443

commit 52b36c921ee59ec69deefb6123cbdb1b91dc3bc7
Merge: 90df5f971e7988a7a024ce95fc83dd9dea3c9b6f 46cd22e159df5387c036e285e8398e9f1320e2b4
Author: Jelte Jansen <jelte at isc.org>
Date:   Tue Mar 27 11:12:57 2012 +0200

    [1443] Merge branch 'master' into merge1443
    
    two options have been added to boss's __init__(), conflict resolved.
    Conflicts:
    	src/bin/bind10/bind10_src.py.in
    	src/bin/bind10/tests/bind10_test.py.in

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                      |   11 ++++-
 src/bin/bind10/bind10_src.py.in                |   19 ++++++--
 src/bin/bind10/tests/bind10_test.py.in         |    6 +++
 src/bin/cfgmgr/b10-cfgmgr.py.in                |    7 +++-
 src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in     |   15 ++++++-
 src/lib/python/isc/config/cfgmgr.py            |   29 +++++++++++-
 src/lib/python/isc/config/cfgmgr_messages.mes  |    6 ++-
 src/lib/python/isc/config/tests/cfgmgr_test.py |   57 +++++++++++++++++++++++-
 8 files changed, 138 insertions(+), 12 deletions(-)

-----------------------------------------------------------------------
diff --git a/ChangeLog b/ChangeLog
index b9840be..1502866 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,13 @@
-411.   [func]          muks
+412.	[func]		jelte
+	Added a command-line option '--clear-config' to bind10, which causes
+	the system to create a backup of the existing configuration database
+	file, and start out with a clean default configuration. This can be
+	used if the configuration file is corrupted to the point where it
+	cannot be read anymore, and BIND 10 refuses to start. The name of
+	the backup file can be found in the logs (CFGMGR_RENAMED_CONFIG_FILE).
+	(Trac #1443, git 52b36c921ee59ec69deefb6123cbdb1b91dc3bc7)
+
+411.	[func]		muks
 	Add a -i/--no-kill command-line argument to bind10, which stops
 	it from sending SIGTERM and SIGKILL to other b10 processes when
 	they're shutting down.
diff --git a/src/bin/bind10/bind10_src.py.in b/src/bin/bind10/bind10_src.py.in
index d002906..37b845d 100755
--- a/src/bin/bind10/bind10_src.py.in
+++ b/src/bin/bind10/bind10_src.py.in
@@ -167,8 +167,9 @@ class BoB:
     """Boss of BIND class."""
     
     def __init__(self, msgq_socket_file=None, data_path=None,
-    config_filename=None, nocache=False, verbose=False, nokill=False,
-    setuid=None, username=None, cmdctl_port=None, wait_time=10):
+                 config_filename=None, clear_config=False, nocache=False,
+                 verbose=False, nokill=False, setuid=None, username=None,
+                 cmdctl_port=None, wait_time=10):
         """
             Initialize the Boss of BIND. This is a singleton (only one can run).
         
@@ -211,6 +212,7 @@ class BoB:
         self.nokill = nokill
         self.data_path = data_path
         self.config_filename = config_filename
+        self.clear_config = clear_config
         self.cmdctl_port = cmdctl_port
         self.wait_time = wait_time
         self._component_configurator = isc.bind10.component.Configurator(self,
@@ -466,6 +468,8 @@ class BoB:
             args.append("--data-path=" + self.data_path)
         if self.config_filename is not None:
             args.append("--config-filename=" + self.config_filename)
+        if self.clear_config:
+            args.append("--clear-config")
         bind_cfgd = ProcessInfo("b10-cfgmgr", args,
                                 self.c_channel_env)
         bind_cfgd.spawn()
@@ -1060,6 +1064,10 @@ def parse_args(args=sys.argv[1:], Parser=OptionParser):
     parser.add_option("-c", "--config-file", action="store",
                       dest="config_file", default=None,
                       help="Configuration database filename")
+    parser.add_option("--clear-config", action="store_true",
+                      dest="clear_config", default=False,
+                      help="Create backup of the configuration file and " +
+                           "start with a clean configuration")
     parser.add_option("-p", "--data-path", dest="data_path",
                       help="Directory to search for configuration files",
                       default=None)
@@ -1172,9 +1180,10 @@ def main():
     try:
         # Go bob!
         boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
-                           options.config_file, options.nocache,
-                           options.verbose, options.nokill, setuid, username,
-                           options.cmdctl_port, options.wait_time)
+                           options.config_file, options.clear_config,
+                           options.nocache, options.verbose, options.nokill,
+                           setuid, username, options.cmdctl_port,
+                           options.wait_time)
         startup_result = boss_of_bind.startup()
         if startup_result:
             logger.fatal(BIND10_STARTUP_ERROR, startup_result)
diff --git a/src/bin/bind10/tests/bind10_test.py.in b/src/bin/bind10/tests/bind10_test.py.in
index fa01d11..0f397ae 100644
--- a/src/bin/bind10/tests/bind10_test.py.in
+++ b/src/bin/bind10/tests/bind10_test.py.in
@@ -1012,6 +1012,12 @@ class TestParseArgs(unittest.TestCase):
         options = parse_args(['--config-file=config-file'], TestOptParser)
         self.assertEqual('config-file', options.config_file)
 
+    def test_clear_config(self):
+        options = parse_args([], TestOptParser)
+        self.assertEqual(False, options.clear_config)
+        options = parse_args(['--clear-config'], TestOptParser)
+        self.assertEqual(True, options.clear_config)
+
     def test_nokill(self):
         options = parse_args([], TestOptParser)
         self.assertEqual(False, options.nokill)
diff --git a/src/bin/cfgmgr/b10-cfgmgr.py.in b/src/bin/cfgmgr/b10-cfgmgr.py.in
index 2ccc430..760b6d8 100755
--- a/src/bin/cfgmgr/b10-cfgmgr.py.in
+++ b/src/bin/cfgmgr/b10-cfgmgr.py.in
@@ -49,6 +49,10 @@ def parse_options(args=sys.argv[1:], Parser=OptionParser):
                       help="Configuration database filename " +
                       "(default=" + DEFAULT_CONFIG_FILE + ")",
                       default=DEFAULT_CONFIG_FILE)
+    parser.add_option("--clear-config", action="store_true",
+                      dest="clear_config", default=False,
+                      help="Back up the configuration file and start with " +
+                           "a clean one")
     (options, args) = parser.parse_args(args)
     if args:
         parser.error("No non-option arguments allowed")
@@ -85,7 +89,8 @@ def main():
     options = parse_options()
     global cm
     try:
-        cm = ConfigManager(options.data_path, options.config_file)
+        cm = ConfigManager(options.data_path, options.config_file,
+                           None, options.clear_config)
         signal.signal(signal.SIGINT, signal_handler)
         signal.signal(signal.SIGTERM, signal_handler)
         cm.read_config()
diff --git a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
index ea5fc8b..ca91c9c 100644
--- a/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
+++ b/src/bin/cfgmgr/tests/b10-cfgmgr_test.py.in
@@ -24,12 +24,13 @@ import bind10_config
 from isc.testutils.parse_args import OptsError, TestOptParser
 
 class MyConfigManager:
-    def __init__(self, path, filename):
+    def __init__(self, path, filename, session=None, rename_config_file=False):
         self._path = path
         self.read_config_called = False
         self.notify_boss_called = False
         self.run_called = False
         self.write_config_called = False
+        self.rename_config_called = False
         self.running = True
         self.virtual_modules = []
 
@@ -45,6 +46,9 @@ class MyConfigManager:
     def write_config(self):
         self.write_config_called = True
 
+    def rename_config_file(self, ofile, nfile):
+        self.rename_config_called = True
+
     def set_virtual_module(self, spec, function):
         self.virtual_modules.append((spec, function))
 
@@ -90,6 +94,7 @@ class TestConfigManagerStartup(unittest.TestCase):
         self.assertTrue(self.loaded_plugins)
         # if there are no changes, config is not written
         self.assertFalse(b.cm.write_config_called)
+        self.assertFalse(b.cm.rename_config_called)
 
         self.assertTrue(b.cm.running)
         b.signal_handler(None, None)
@@ -187,6 +192,14 @@ class TestParseArgs(unittest.TestCase):
         self.assertRaises(OptsError, b.parse_options, ['--config-filename'],
                           TestOptParser)
 
+    def test_clear_config(self):
+        b = __import__("b10-cfgmgr")
+        parsed = b.parse_options([], TestOptParser)
+        self.assertFalse(parsed.clear_config)
+        parsed = b.parse_options(['--clear-config'], TestOptParser)
+        self.assertTrue(parsed.clear_config)
+        
+
 if __name__ == '__main__':
     unittest.main()
 
diff --git a/src/lib/python/isc/config/cfgmgr.py b/src/lib/python/isc/config/cfgmgr.py
index dd97827..8934de8 100644
--- a/src/lib/python/isc/config/cfgmgr.py
+++ b/src/lib/python/isc/config/cfgmgr.py
@@ -148,6 +148,27 @@ class ConfigManagerData:
             # Ok if we really can't delete it anymore, leave it
             pass
 
+    def rename_config_file(self, old_file_name=None, new_file_name=None):
+        """Renames the given configuration file to the given new file name,
+           if it exists. If it does not exist, nothing happens.
+           If old_file_name is None (default), the file used in
+           read_from_file is used. If new_file_name is None (default), the
+           file old_file_name appended with .bak is used. If that file exists
+           already, .1 is appended. If that file exists, .2 is appended, etc.
+        """
+        if old_file_name is None:
+            old_file_name = self.db_filename
+        if new_file_name is None:
+            new_file_name = old_file_name + ".bak"
+        if os.path.exists(new_file_name):
+            i = 1
+            while os.path.exists(new_file_name + "." + str(i)):
+                i += 1
+            new_file_name = new_file_name + "." + str(i)
+        if os.path.exists(old_file_name):
+            logger.info(CFGMGR_RENAMED_CONFIG_FILE, old_file_name, new_file_name)
+            os.rename(old_file_name, new_file_name)
+
     def __eq__(self, other):
         """Returns True if the data contained is equal. data_path and
            db_filename may be different."""
@@ -163,14 +184,16 @@ class ConfigManager:
        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, database_filename, session=None):
+    def __init__(self, data_path, database_filename, session=None,
+                 clear_config=False):
         """Initialize the configuration manager. The data_path string
            is the path to the directory where the configuration is
            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. If clear_config is True, the configuration file is
+           renamed and a new one is created."""
         self.data_path = data_path
         self.database_filename = database_filename
         self.module_specs = {}
@@ -179,6 +202,8 @@ class ConfigManager:
         # of some other process
         self.virtual_modules = {}
         self.config = ConfigManagerData(data_path, database_filename)
+        if clear_config:
+            self.config.rename_config_file()
         if session:
             self.cc = session
         else:
diff --git a/src/lib/python/isc/config/cfgmgr_messages.mes b/src/lib/python/isc/config/cfgmgr_messages.mes
index 61a63ed..ad78be0 100644
--- a/src/lib/python/isc/config/cfgmgr_messages.mes
+++ b/src/lib/python/isc/config/cfgmgr_messages.mes
@@ -51,7 +51,11 @@ error is given. The most likely cause is that the system does not have
 write access to the configuration database file. The updated
 configuration is not stored.
 
+% CFGMGR_RENAMED_CONFIG_FILE renamed configuration file %1 to %2, will create new %1
+BIND 10 has been started with the command to clear the configuration file.
+The existing file is backed up to the given file name, so that data is not
+immediately lost if this was done by accident.
+
 % CFGMGR_STOPPED_BY_KEYBOARD keyboard interrupt, shutting down
 There was a keyboard interrupt signal to stop the cfgmgr daemon. The
 daemon will now shut down.
-
diff --git a/src/lib/python/isc/config/tests/cfgmgr_test.py b/src/lib/python/isc/config/tests/cfgmgr_test.py
index 7fe8212..891a7d7 100644
--- a/src/lib/python/isc/config/tests/cfgmgr_test.py
+++ b/src/lib/python/isc/config/tests/cfgmgr_test.py
@@ -74,6 +74,60 @@ class TestConfigManagerData(unittest.TestCase):
         self.assertEqual(self.config_manager_data, new_config)
         os.remove(output_file_name)
 
+    def check_existence(self, files, should_exist=[], should_not_exist=[]):
+        """Helper function for test_rename_config_file.
+           Arguments:
+           files: array of file names to check.
+           should_exist: array of indices, the files in 'files' with these
+                         indices should exist.
+           should_not_exist: array of indices, the files in 'files' with
+                             these indices should not exist."""
+        for n in should_exist:
+            self.assertTrue(os.path.exists(files[n]))
+        for n in should_not_exist:
+            self.assertFalse(os.path.exists(files[n]))
+
+    def test_rename_config_file(self):
+        # test file names, put in array for easy cleanup
+        filenames = [ "b10-config-rename-test",
+                      "b10-config-rename-test.bak",
+                      "b10-config-rename-test.bak.1",
+                      "b10-config-rename-test.bak.2" ]
+
+        for filename in filenames:
+            if os.path.exists(filename):
+                os.remove(filename)
+
+        # The original does not exist, so the new one should not be created
+        self.config_manager_data.rename_config_file(filenames[0])
+        self.check_existence(filenames, [], [0, 1, 2, 3])
+
+        # now create a file to rename, and call rename again
+        self.config_manager_data.write_to_file(filenames[0])
+        self.config_manager_data.rename_config_file(filenames[0])
+        self.check_existence(filenames, [1], [0, 2, 3])
+
+        # If backup already exists, give it a new name automatically
+        self.config_manager_data.write_to_file(filenames[0])
+        self.config_manager_data.rename_config_file(filenames[0])
+        self.check_existence(filenames, [1, 2], [0, 3])
+
+        # If backup already exists, give it a new name automatically with
+        # increasing postfix
+        self.config_manager_data.write_to_file(filenames[0])
+        self.config_manager_data.rename_config_file(filenames[0])
+        self.check_existence(filenames, [1, 2, 3], [0])
+
+        # Test with explicit renamed file argument
+        self.config_manager_data.rename_config_file(filenames[1],
+                                                    filenames[0])
+        self.check_existence(filenames, [0, 2, 3], [1])
+
+        # clean up again to be nice
+        for filename in filenames:
+            if os.path.exists(filename):
+                os.remove(filename)
+
     def test_equality(self):
         # tests the __eq__ function. Equality is only defined
         # by equality of the .data element. If data_path or db_filename
@@ -570,5 +624,6 @@ if __name__ == '__main__':
     if not 'CONFIG_TESTDATA_PATH' in os.environ or not 'CONFIG_WR_TESTDATA_PATH' in os.environ:
         print("You need to set the environment variable CONFIG_TESTDATA_PATH and CONFIG_WR_TESTDATA_PATH to point to the directory containing the test data files")
         exit(1)
+    isc.log.init("unittests")
+    isc.log.resetUnitTestRootLogger()
     unittest.main()
-



More information about the bind10-changes mailing list