docs: def _update_existing_routes(route_spec, failed_ips, questionable_ips,
                              vpc_info, con, routes_in_rts):
      """
      Go over the existing routes and check whether they still match the spec.
  
      If the chosen router has failed or is questionable or is not in the host
      list anymore, the route needs to be updated. If the CIDR isn't in the spec
      at all anymore then it needs to be deleted.
  
      Keeps track of the routes we have seen in each RT and populates the
      passed-in routes_in_rts dictionary with that info.
  
      Returns a dict with the routers chosen for the various routes we
      encountered.
  
      """
      chosen_routers = {}              # keep track of chosen routers for CIDRs
      NONE_HEALTHY   = "none-healthy"  # used as marker in chosen_routers
      for rt in vpc_info['route_tables']:
          # Check whether this RT is associated with cluster node subnets. If it
          # is not then we will perform NO operations on this RT. We will also
          # not collect the routes from this RT.
          rt_subnets           = \
                      set(vpc_info.get('rt_subnet_lookup', {}).get(rt.id, []))
          cluster_node_subnets = \
                      set(vpc_info.get('cluster_node_subnets', []))
          if cluster_node_subnets and \
                          (not rt_subnets or
                           not rt_subnets.intersection(cluster_node_subnets)):
              logging.debug("Skipping processing of RT '%s' since RT's "
                            "subnets (%s) are not part of cluster (%s)." %
                            (rt.id,
                             ", ".join(rt_subnets) if rt_subnets else "none",
                             ", ".join(cluster_node_subnets)))
              continue
  
          routes_in_rts[rt.id] = []
          # Iterate over all the routes we find in each RT
          for r in rt.routes:
              dcidr = r.destination_cidr_block
  
              if _is_cidr_in_ignore_routes(dcidr):
                  # A list of CIDRs may have been specified on the command line
                  # via the --ignore_routes option. If the destination CIDR of
                  # this route here is contained in any of those specified CIDRs
                  # then we will not touch or change this route. Often this is
                  # used to protect routes to special instances, such as
                  # proxies or NAT instances.
                  _rt_state_update(rt.id, dcidr,
                                   msg="Ignored: Protected CIDR.")
                  continue
  
              if r.instance_id is None and r.interface_id is None:
                  # There are some routes already present in the route table,
                  # which we don't need to mess with. Specifically, routes that
                  # aren't attached to a particular instance or interface.
                  # We skip those.
                  _rt_state_update(rt.id, dcidr,
                                   msg="Ignored: Not a route to an instance")
                  continue
  
              routes_in_rts[rt.id].append(dcidr)  # remember we've seen the route
  
              hosts = route_spec.get(dcidr)       # eligible routers for CIDR
  
              # Current router host for this CIDR/route.
              inst_id, ipaddr, eni_id = \
                                  _get_host_for_route(vpc_info, r, rt, dcidr)
  
              if not hosts:
                  # The route isn't in the spec anymore and should be
                  # deleted.
                  logging.info("--- route not in spec, deleting in "
                               "RT '%s': %s -> ... (%s, %s)" %
                               (rt.id, dcidr, inst_id, eni_id))
  
                  con.delete_route(route_table_id         = rt.id,
                                   destination_cidr_block = dcidr)
                  if dcidr in CURRENT_STATE.routes:
                      del CURRENT_STATE.routes[dcidr]
  
                  continue
  
              # We have a route and it's still in the spec. Do we need to update
              # the router? Multiple reasons for that:
              # - Router is not in the list of eligible hosts anymore
              # - Router has failed or is questionable
              # - In a different route table we used a different host as the
              #   router.
  
              # Seen this route in another RT before? This will be None if we'be
              # not seen it before, and will be NONE_HEALTHY, but we couldn't
              # find a single healthy eligible router host for it.
              stored_router_ip = chosen_routers.get(dcidr)
  
              # Has our current router failed or is questionable?
              ipaddr_should_be_replaced = ipaddr in failed_ips or \
                                          ipaddr in questionable_ips
  
              # Is the host not eligible anymore?
              ipaddr_not_eligible = ipaddr not in hosts
  
              shouldnt_use_ipaddr = \
                              ipaddr_should_be_replaced or ipaddr_not_eligible
  
              if (not stored_router_ip or stored_router_ip == ipaddr) and \
                                                      not shouldnt_use_ipaddr:
                  # Haven't seen it before, or points to same router AND
                  # router is healthy: All good
                  if not stored_router_ip:
                      # Remember this IP as a suitable router for CIDR
                      chosen_routers[dcidr] = ipaddr
                  logging.info("--- route exists already in RT '%s': "
                               "%s -> %s (%s, %s)" %
                               (rt.id, dcidr,
                                ipaddr, inst_id, eni_id))
                  CURRENT_STATE.routes[dcidr] = (ipaddr, inst_id, eni_id)
                  _rt_state_update(rt.id, dcidr, ipaddr, inst_id, eni_id,
                                   msg="Current: Route exist and up to date")
                  continue
  
              if stored_router_ip == NONE_HEALTHY:
                  # We've tried to set a route for this before, but
                  # couldn't find any healthy hosts. Can't do anything and
                  # need to skip.
                  CURRENT_STATE.routes[dcidr] = (ipaddr, inst_id, eni_id)
                  _rt_state_update(rt.id, dcidr, ipaddr, inst_id, eni_id,
                                   msg="None healthy, black hole: "
                                       "Determined earlier")
                  continue
  
              if stored_router_ip:
                  # Just use the router we've seen before. We know that'll work,
                  # because only healthy hosts make it into the chosen_routers
                  # dict.
                  new_router_ip = stored_router_ip
                  update_reason = "other RT used different IP"
              else:
                  # Haven't seen this route in another RT, so we'll
                  # choose a new router
                  new_router_ip = _choose_different_host(ipaddr, hosts,
                                                         failed_ips,
                                                         questionable_ips)
                  if new_router_ip is None:
                      # Couldn't find healthy host to be router, forced
                      # to skip this one.
                      CURRENT_STATE.routes[dcidr] = (ipaddr, inst_id, eni_id)
                      chosen_routers[dcidr] = NONE_HEALTHY
                      logging.warning("--- cannot find available target "
                                      "for route update %s! "
                                      "Nothing I can do..." % (dcidr))
                      continue
  
                  chosen_routers[dcidr] = new_router_ip
                  update_reason = "old IP failed/questionable or " \
                                  "not eligible anymore"
  
              _update_route(dcidr, new_router_ip, ipaddr,
                            vpc_info, con, rt.id, update_reason)
  
      return chosen_routers