Monitor queues to receive updates about new route specs or any detected
    failed IPs.

    If any of those have updates, notify the health-monitor thread with a
    message on a special queue and also re-process the entire routing table.

    The 'iterations' argument allows us to limit the running time of the watch
    loop for test purposes. Not used during normal operation. Also, for faster
    tests, sleep_time can be set to values less than 1.

    The 'route_check_time_interval' arguments specifies the number of seconds
    we allow to elapse before forcing a re-check of the VPC routes. This is so
    that accidentally deleted routes or manually broken route tables can be
    fixed back up again on their own.

def _event_monitor_loop(region_name, vpc_id,
                        watcher_plugin, health_plugin,
                        iterations, sleep_time,
                        route_check_time_interval=30):
    """
    Monitor queues to receive updates about new route specs or any detected
    failed IPs.

    If any of those have updates, notify the health-monitor thread with a
    message on a special queue and also re-process the entire routing table.

    The 'iterations' argument allows us to limit the running time of the watch
    loop for test purposes. Not used during normal operation. Also, for faster
    tests, sleep_time can be set to values less than 1.

    The 'route_check_time_interval' arguments specifies the number of seconds
    we allow to elapse before forcing a re-check of the VPC routes. This is so
    that accidentally deleted routes or manually broken route tables can be
    fixed back up again on their own.

    """
    q_route_spec = watcher_plugin.get_route_spec_queue()
    q_monitor_ips, q_failed_ips, q_questionable_ips = \
                                                health_plugin.get_queues()
    time.sleep(sleep_time)   # Wait to allow monitor to report results

    current_route_spec = {}  # The last route spec we have seen
    all_ips = []             # Cache of IP addresses we currently know about

    # Occasionally we want to recheck VPC routes even without other updates.
    # That way, if a route is manually deleted by someone, it will be
    # re-created on its own.
    last_route_check_time    = time.time()
    while not CURRENT_STATE._stop_all:
        try:
            # Get the latest messages from the route-spec monitor and the
            # health-check monitor. At system start the route-spec queue should
            # immediately have been initialized with a first message.
            failed_ips     = utils.read_last_msg_from_queue(q_failed_ips)
            questnbl_ips   = utils.read_last_msg_from_queue(q_questionable_ips)
            new_route_spec = utils.read_last_msg_from_queue(q_route_spec)

            if failed_ips:
                # Store the failed IPs in the shared state
                CURRENT_STATE.failed_ips = failed_ips

            if questnbl_ips:
                # Store the questionable IPs in the shared state
                CURRENT_STATE.questionble_ips = questnbl_ips

            if new_route_spec:
                # Store the new route spec in the shared state
                CURRENT_STATE.route_spec = new_route_spec
                current_route_spec = new_route_spec
                # Need to communicate a new set of IPs to the health
                # monitoring thread, in case the list changed. The list of
                # addresses is extracted from the route spec. Pass in the old
                # version of the address list, so that this function can
                # compare to see if there are any changes to the host list.
                all_ips = _update_health_monitor_with_new_ips(new_route_spec,
                                                              all_ips,
                                                              q_monitor_ips)

            # Spec or list of failed or questionable IPs changed? Update
            # routes...
            # We pass in the last route spec we have seen, since we are also
            # here in case we only have failed/questionable IPs, but no new
            # route spec. This is also called occasionally on its own, so that
            # we can repair any damaged route tables in VPC.
            now = time.time()
            time_for_regular_recheck = \
                    (now - last_route_check_time) > route_check_time_interval

            if new_route_spec or failed_ips or questnbl_ips or \
                                                time_for_regular_recheck:
                if not new_route_spec and not (failed_ips or questnbl_ips):
                    # Only reason we are here is due to expired timer.
                    logging.debug("Time for regular route check")

                last_route_check_time = now
                vpc.handle_spec(region_name, vpc_id, current_route_spec,
                                failed_ips if failed_ips else [],
                                questnbl_ips if questnbl_ips else [])

            # If iterations are provided, count down and exit
            if iterations is not None:
                iterations -= 1
                if iterations == 0:
                    break

            time.sleep(sleep_time)
        except KeyboardInterrupt:
            # Allow exit via keyboard interrupt, useful during development
            return
        except Exception as e:
            # Of course we should never get here, but if we do, better to log
            # it and keep operating best we can...
            import traceback
            traceback.print_exc()
            logging.error("*** Uncaught exception 1: %s" % str(e))
            return

    logging.debug("event_monitor_loop ended: Global stop")