|
@@ -16,7 +16,7 @@
|
|
|
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
|
|
|
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
|
-"""\
|
|
|
+"""
|
|
|
This file implements the Secondary Manager program.
|
|
|
|
|
|
The secondary manager is one of the co-operating processes
|
|
@@ -92,15 +92,19 @@ class ZonemgrRefresh:
|
|
|
"""This class will maintain and manage zone refresh info.
|
|
|
It also provides methods to keep track of zone timers and
|
|
|
do zone refresh.
|
|
|
+ Zone timers can be started by calling run_timer(), and it
|
|
|
+ can be stopped by calling shutdown() in another thread.
|
|
|
+
|
|
|
"""
|
|
|
|
|
|
def __init__(self, cc, db_file, slave_socket, config_data):
|
|
|
self._cc = cc
|
|
|
- self._socket = slave_socket
|
|
|
+ self._check_sock = slave_socket
|
|
|
self._db_file = db_file
|
|
|
self.update_config_data(config_data)
|
|
|
self._zonemgr_refresh_info = {}
|
|
|
self._build_zonemgr_refresh_info()
|
|
|
+ self._running = False
|
|
|
|
|
|
def _random_jitter(self, max, jitter):
|
|
|
"""Imposes some random jitters for refresh and
|
|
@@ -319,40 +323,86 @@ class ZonemgrRefresh:
|
|
|
|
|
|
return False
|
|
|
|
|
|
- def run_timer(self):
|
|
|
- """Keep track of zone timers."""
|
|
|
- while True:
|
|
|
- # Zonemgr has no zone.
|
|
|
+ def _run_timer(self, start_event):
|
|
|
+ start_event.set()
|
|
|
+ while self._running:
|
|
|
+ # If zonemgr has no zone, set timer timeout to self._lowerbound_retry.
|
|
|
if self._zone_mgr_is_empty():
|
|
|
- time.sleep(self._lowerbound_retry) # A better time?
|
|
|
- continue
|
|
|
-
|
|
|
- zone_need_refresh = self._find_need_do_refresh_zone()
|
|
|
- # If don't get zone with minimum next refresh time, set timer timeout = lowerbound_retry
|
|
|
- if not zone_need_refresh:
|
|
|
timeout = self._lowerbound_retry
|
|
|
else:
|
|
|
- timeout = self._get_zone_next_refresh_time(zone_need_refresh) - self._get_current_time()
|
|
|
- if (timeout < 0):
|
|
|
- self._do_refresh(zone_need_refresh)
|
|
|
- continue
|
|
|
+ zone_need_refresh = self._find_need_do_refresh_zone()
|
|
|
+ # If don't get zone with minimum next refresh time, set timer timeout to self._lowerbound_retry
|
|
|
+ if not zone_need_refresh:
|
|
|
+ timeout = self._lowerbound_retry
|
|
|
+ else:
|
|
|
+ timeout = self._get_zone_next_refresh_time(zone_need_refresh) - self._get_current_time()
|
|
|
+ if (timeout < 0):
|
|
|
+ self._do_refresh(zone_need_refresh)
|
|
|
+ continue
|
|
|
|
|
|
""" Wait for the socket notification for a maximum time of timeout
|
|
|
in seconds (as float)."""
|
|
|
try:
|
|
|
- (rlist, wlist, xlist) = select.select([self._socket], [], [], timeout)
|
|
|
- if rlist:
|
|
|
- self._socket.recv(32)
|
|
|
- except ValueError as e:
|
|
|
- raise ZonemgrException("[b10-zonemgr] Socket has been closed\n")
|
|
|
- break
|
|
|
+ rlist, wlist, xlist = select.select([self._check_sock, self._read_sock], [], [], timeout)
|
|
|
except select.error as e:
|
|
|
if e.args[0] == errno.EINTR:
|
|
|
(rlist, wlist, xlist) = ([], [], [])
|
|
|
else:
|
|
|
- raise ZonemgrException("[b10-zonemgr] Error with select(): %s\n" % e)
|
|
|
+ sys.stderr.write("[b10-zonemgr] Error with select(); %s\n" % e)
|
|
|
break
|
|
|
|
|
|
+ for fd in rlist:
|
|
|
+ if fd == self._read_sock: # awaken by shutdown socket
|
|
|
+ # self._running will be False by now, if it is not a false
|
|
|
+ # alarm
|
|
|
+ continue
|
|
|
+ if fd == self._check_sock: # awaken by check socket
|
|
|
+ self._check_sock.recv(32)
|
|
|
+
|
|
|
+ def run_timer(self, daemon=False):
|
|
|
+ """
|
|
|
+ Keep track of zone timers. Spawns and starts a thread. The thread object is returned.
|
|
|
+
|
|
|
+ You can stop it by calling shutdown().
|
|
|
+ """
|
|
|
+ # Small sanity check
|
|
|
+ if self._running:
|
|
|
+ raise RuntimeError("Trying to run the timers twice at the same time")
|
|
|
+
|
|
|
+ # Prepare the launch
|
|
|
+ self._running = True
|
|
|
+ (self._read_sock, self._write_sock) = socket.socketpair()
|
|
|
+ start_event = threading.Event()
|
|
|
+
|
|
|
+ # Start the thread
|
|
|
+ self._thread = threading.Thread(target = self._run_timer,
|
|
|
+ args = (start_event,))
|
|
|
+ if daemon:
|
|
|
+ self._thread.setDaemon(True)
|
|
|
+ self._thread.start()
|
|
|
+ start_event.wait()
|
|
|
+
|
|
|
+ # Return the thread to anyone interested
|
|
|
+ return self._thread
|
|
|
+
|
|
|
+ def shutdown(self):
|
|
|
+ """
|
|
|
+ Stop the run_timer() thread. Block until it finished. This must be
|
|
|
+ called from a different thread.
|
|
|
+ """
|
|
|
+ if not self._running:
|
|
|
+ raise RuntimeError("Trying to shutdown, but not running")
|
|
|
+
|
|
|
+ # Ask the thread to stop
|
|
|
+ self._running = False
|
|
|
+ self._write_sock.send(b'shutdown') # make self._read_sock readble
|
|
|
+ # Wait for it to actually finnish
|
|
|
+ self._thread.join()
|
|
|
+ # Wipe out what we do not need
|
|
|
+ self._thread = None
|
|
|
+ self._read_sock = None
|
|
|
+ self._write_sock = None
|
|
|
+
|
|
|
def update_config_data(self, new_config):
|
|
|
""" update ZonemgrRefresh config """
|
|
|
self._lowerbound_refresh = new_config.get('lowerbound_refresh')
|
|
@@ -360,7 +410,6 @@ class ZonemgrRefresh:
|
|
|
self._max_transfer_timeout = new_config.get('max_transfer_timeout')
|
|
|
self._jitter_scope = new_config.get('jitter_scope')
|
|
|
|
|
|
-
|
|
|
class Zonemgr:
|
|
|
"""Zone manager class."""
|
|
|
def __init__(self):
|
|
@@ -370,16 +419,11 @@ class Zonemgr:
|
|
|
# Create socket pair for communicating between main thread and zonemgr timer thread
|
|
|
self._master_socket, self._slave_socket = socket.socketpair(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
|
self._zone_refresh = ZonemgrRefresh(self._cc, self._db_file, self._slave_socket, self._config_data)
|
|
|
- self._start_zone_refresh_timer()
|
|
|
+ self._zone_refresh.run_timer()
|
|
|
|
|
|
self._lock = threading.Lock()
|
|
|
self._shutdown_event = threading.Event()
|
|
|
-
|
|
|
- def _start_zone_refresh_timer(self):
|
|
|
- """Start a new thread to keep track of zone timers"""
|
|
|
- listener = threading.Thread(target = self._zone_refresh.run_timer, args = ())
|
|
|
- listener.setDaemon(True)
|
|
|
- listener.start()
|
|
|
+ self.running = False
|
|
|
|
|
|
def _setup_session(self):
|
|
|
"""Setup two sessions for zonemgr, one(self._module_cc) is used for receiving
|
|
@@ -407,15 +451,12 @@ class Zonemgr:
|
|
|
"""Shutdown the zonemgr process. the thread which is keeping track of zone
|
|
|
timers should be terminated.
|
|
|
"""
|
|
|
+ self._zone_refresh.shutdown()
|
|
|
+
|
|
|
self._slave_socket.close()
|
|
|
self._master_socket.close()
|
|
|
-
|
|
|
self._shutdown_event.set()
|
|
|
- main_thread = threading.currentThread()
|
|
|
- for th in threading.enumerate():
|
|
|
- if th is main_thread:
|
|
|
- continue
|
|
|
- th.join()
|
|
|
+ self.running = False
|
|
|
|
|
|
def config_handler(self, new_config):
|
|
|
""" Update config data. """
|
|
@@ -474,21 +515,21 @@ class Zonemgr:
|
|
|
with self._lock:
|
|
|
self._zone_refresh.zone_handle_notify(zone_name_class, master)
|
|
|
# Send notification to zonemgr timer thread
|
|
|
- self._master_socket.send(b" ")
|
|
|
+ self._master_socket.send(b" ")# make self._slave_socket readble
|
|
|
|
|
|
elif command == ZONE_XFRIN_SUCCESS_COMMAND:
|
|
|
""" Handle xfrin success command"""
|
|
|
zone_name_class = self._parse_cmd_params(args, command)
|
|
|
with self._lock:
|
|
|
self._zone_refresh.zone_refresh_success(zone_name_class)
|
|
|
- self._master_socket.send(b" ")
|
|
|
+ self._master_socket.send(b" ")# make self._slave_socket readble
|
|
|
|
|
|
elif command == ZONE_XFRIN_FAILED_COMMAND:
|
|
|
""" Handle xfrin fail command"""
|
|
|
zone_name_class = self._parse_cmd_params(args, command)
|
|
|
with self._lock:
|
|
|
self._zone_refresh.zone_refresh_fail(zone_name_class)
|
|
|
- self._master_socket.send(b" ")
|
|
|
+ self._master_socket.send(b" ")# make self._slave_socket readble
|
|
|
|
|
|
elif command == "shutdown":
|
|
|
self.shutdown()
|
|
@@ -499,6 +540,7 @@ class Zonemgr:
|
|
|
return answer
|
|
|
|
|
|
def run(self):
|
|
|
+ self.running = True
|
|
|
while not self._shutdown_event.is_set():
|
|
|
self._module_cc.check_command(False)
|
|
|
|
|
@@ -538,6 +580,6 @@ if '__main__' == __name__:
|
|
|
except isc.config.ModuleCCSessionError as e:
|
|
|
sys.stderr.write("[b10-zonemgr] exit zonemgr process: %s\n" % str(e))
|
|
|
|
|
|
- if zonemgrd:
|
|
|
+ if zonemgrd and zonemgrd.running:
|
|
|
zonemgrd.shutdown()
|
|
|
|