Browse Source

Implement "--brittle" option.

Shane Kerr 14 years ago
parent
commit
3ceeab28d4
2 changed files with 48 additions and 4 deletions
  1. 12 4
      src/bin/bind10/bind10.py.in
  2. 36 0
      src/bin/bind10/tests/bind10_test.py.in

+ 12 - 4
src/bin/bind10/bind10.py.in

@@ -202,7 +202,7 @@ class BoB:
     
     
     def __init__(self, msgq_socket_file=None, data_path=None,
     def __init__(self, msgq_socket_file=None, data_path=None,
     config_filename=None, nocache=False, verbose=False, setuid=None,
     config_filename=None, nocache=False, verbose=False, setuid=None,
-    username=None, cmdctl_port=None):
+    username=None, cmdctl_port=None, brittle=False):
         """
         """
             Initialize the Boss of BIND. This is a singleton (only one can run).
             Initialize the Boss of BIND. This is a singleton (only one can run).
         
         
@@ -235,6 +235,7 @@ class BoB:
         self.data_path = data_path
         self.data_path = data_path
         self.config_filename = config_filename
         self.config_filename = config_filename
         self.cmdctl_port = cmdctl_port
         self.cmdctl_port = cmdctl_port
+        self.brittle = brittle
 
 
     def config_handler(self, new_config):
     def config_handler(self, new_config):
         # If this is initial update, don't do anything now, leave it to startup
         # If this is initial update, don't do anything now, leave it to startup
@@ -710,20 +711,22 @@ class BoB:
         if self.verbose:
         if self.verbose:
             sys.stdout.write("[bind10] All processes ended, server done.\n")
             sys.stdout.write("[bind10] All processes ended, server done.\n")
 
 
+    def _get_process_exit_status(self):
+        return os.waitpid(-1, os.WNOHANG)
+
     def reap_children(self):
     def reap_children(self):
         """Check to see if any of our child processes have exited, 
         """Check to see if any of our child processes have exited, 
         and note this for later handling. 
         and note this for later handling. 
         """
         """
         while True:
         while True:
             try:
             try:
-                (pid, exit_status) = os.waitpid(-1, os.WNOHANG)
+                (pid, exit_status) = self._get_process_exit_status()
             except OSError as o:
             except OSError as o:
                 if o.errno == errno.ECHILD: break
                 if o.errno == errno.ECHILD: break
                 # XXX: should be impossible to get any other error here
                 # XXX: should be impossible to get any other error here
                 raise
                 raise
             if pid == 0: break
             if pid == 0: break
             if pid in self.processes:
             if pid in self.processes:
-
                 # One of the processes we know about.  Get information on it.
                 # One of the processes we know about.  Get information on it.
                 proc_info = self.processes.pop(pid)
                 proc_info = self.processes.pop(pid)
                 proc_info.restart_schedule.set_run_stop_time()
                 proc_info.restart_schedule.set_run_stop_time()
@@ -747,6 +750,11 @@ class BoB:
                         sys.stdout.write(
                         sys.stdout.write(
                                  "[bind10] The b10-msgq process died, shutting down.\n")
                                  "[bind10] The b10-msgq process died, shutting down.\n")
                         self.runnable = False
                         self.runnable = False
+
+                # If we're in 'brittle' mode, we want to shutdown after
+                # any process dies.
+                if self.brittle:
+                    self.runnable = False
             else:
             else:
                 sys.stdout.write("[bind10] Unknown child pid %d exited.\n" % pid)
                 sys.stdout.write("[bind10] Unknown child pid %d exited.\n" % pid)
 
 
@@ -961,7 +969,7 @@ def main():
     # Go bob!
     # Go bob!
     boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
     boss_of_bind = BoB(options.msgq_socket_file, options.data_path,
                        options.config_file, options.nocache, options.verbose,
                        options.config_file, options.nocache, options.verbose,
-                       setuid, username, options.cmdctl_port)
+                       setuid, username, options.cmdctl_port, options.brittle)
     startup_result = boss_of_bind.startup()
     startup_result = boss_of_bind.startup()
     if startup_result:
     if startup_result:
         sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)
         sys.stderr.write("[bind10] Error on startup: %s\n" % startup_result)

+ 36 - 0
src/bin/bind10/tests/bind10_test.py.in

@@ -156,42 +156,52 @@ class MockBob(BoB):
     def start_msgq(self, c_channel_env):
     def start_msgq(self, c_channel_env):
         self.msgq = True
         self.msgq = True
         self.processes[2] = ProcessInfo('b10-msgq', ['/bin/false'])
         self.processes[2] = ProcessInfo('b10-msgq', ['/bin/false'])
+        self.processes[2].pid = 2
 
 
     def start_cfgmgr(self, c_channel_env):
     def start_cfgmgr(self, c_channel_env):
         self.cfgmgr = True
         self.cfgmgr = True
         self.processes[3] = ProcessInfo('b10-cfgmgr', ['/bin/false'])
         self.processes[3] = ProcessInfo('b10-cfgmgr', ['/bin/false'])
+        self.processes[3].pid = 3
 
 
     def start_ccsession(self, c_channel_env):
     def start_ccsession(self, c_channel_env):
         self.ccsession = True
         self.ccsession = True
         self.processes[4] = ProcessInfo('b10-ccsession', ['/bin/false'])
         self.processes[4] = ProcessInfo('b10-ccsession', ['/bin/false'])
+        self.processes[4].pid = 4
 
 
     def start_auth(self, c_channel_env):
     def start_auth(self, c_channel_env):
         self.auth = True
         self.auth = True
         self.processes[5] = ProcessInfo('b10-auth', ['/bin/false'])
         self.processes[5] = ProcessInfo('b10-auth', ['/bin/false'])
+        self.processes[5].pid = 5
 
 
     def start_resolver(self, c_channel_env):
     def start_resolver(self, c_channel_env):
         self.resolver = True
         self.resolver = True
         self.processes[6] = ProcessInfo('b10-resolver', ['/bin/false'])
         self.processes[6] = ProcessInfo('b10-resolver', ['/bin/false'])
+        self.processes[6].pid = 6
 
 
     def start_xfrout(self, c_channel_env):
     def start_xfrout(self, c_channel_env):
         self.xfrout = True
         self.xfrout = True
         self.processes[7] = ProcessInfo('b10-xfrout', ['/bin/false'])
         self.processes[7] = ProcessInfo('b10-xfrout', ['/bin/false'])
+        self.processes[7].pid = 7
 
 
     def start_xfrin(self, c_channel_env):
     def start_xfrin(self, c_channel_env):
         self.xfrin = True
         self.xfrin = True
         self.processes[8] = ProcessInfo('b10-xfrin', ['/bin/false'])
         self.processes[8] = ProcessInfo('b10-xfrin', ['/bin/false'])
+        self.processes[8].pid = 8
 
 
     def start_zonemgr(self, c_channel_env):
     def start_zonemgr(self, c_channel_env):
         self.zonemgr = True
         self.zonemgr = True
         self.processes[9] = ProcessInfo('b10-zonemgr', ['/bin/false'])
         self.processes[9] = ProcessInfo('b10-zonemgr', ['/bin/false'])
+        self.processes[9].pid = 9
 
 
     def start_stats(self, c_channel_env):
     def start_stats(self, c_channel_env):
         self.stats = True
         self.stats = True
         self.processes[10] = ProcessInfo('b10-stats', ['/bin/false'])
         self.processes[10] = ProcessInfo('b10-stats', ['/bin/false'])
+        self.processes[10].pid = 10
 
 
     def start_cmdctl(self, c_channel_env):
     def start_cmdctl(self, c_channel_env):
         self.cmdctl = True
         self.cmdctl = True
         self.processes[11] = ProcessInfo('b10-cmdctl', ['/bin/false'])
         self.processes[11] = ProcessInfo('b10-cmdctl', ['/bin/false'])
+        self.processes[11].pid = 11
 
 
     # We don't really use all of these stop_ methods. But it might turn out
     # We don't really use all of these stop_ methods. But it might turn out
     # someone would add some stop_ method to BoB and we want that one overriden
     # someone would add some stop_ method to BoB and we want that one overriden
@@ -606,5 +616,31 @@ class TestPIDFile(unittest.TestCase):
         self.assertRaises(IOError, dump_pid,
         self.assertRaises(IOError, dump_pid,
                           'nonexistent_dir' + os.sep + 'bind10.pid')
                           'nonexistent_dir' + os.sep + 'bind10.pid')
 
 
+class TestBrittle(unittest.TestCase):
+    def test_brittle_disabled(self):
+        bob = MockBob()
+        bob.start_all_processes()
+        bob.runnable = True
+
+        bob.reap_children()
+        self.assertTrue(bob.runnable)
+
+    def simulated_exit(self):
+        ret_val = self.exit_info
+        self.exit_info = (0, 0)
+        return ret_val
+
+    def test_brittle_enabled(self):
+        bob = MockBob()
+        bob.start_all_processes()
+        bob.runnable = True
+
+        bob.brittle = True
+        self.exit_info = (5, 0)
+        bob._get_process_exit_status = self.simulated_exit
+
+        bob.reap_children()
+        self.assertFalse(bob.runnable)
+
 if __name__ == '__main__':
 if __name__ == '__main__':
     unittest.main()
     unittest.main()