92 lines
3.9 KiB
Python
92 lines
3.9 KiB
Python
import logging
|
|
from typing import Callable, List, Set
|
|
|
|
from amqp.adapter.amq_route_factory import AMQRouteFactory, NULL_ROUTE
|
|
from amqp.model.model import AMQRoute, AMQMessage
|
|
from amqp.router.route_database import RouteDatabase
|
|
from amqp.router.utils import sanitize_as_rabbitmq_name
|
|
|
|
CM_REQUEST_EXCHANGE = "CM-Request"
|
|
CM_RESPONSE_EXCHANGE = "CM-Response"
|
|
CM_C_REQ_QUEUE = "C-REQ"
|
|
CM_C_RESP_QUEUE = "C-RESP"
|
|
CM_C_AMQ_QUEUE = "C-AMQ"
|
|
CM_P_RESP_QUEUE = "P-RESP"
|
|
CM_P_REPLY_TO = "P-REPLY-TO"
|
|
ANY_RECIPIENT = "*"
|
|
AMQ_RPC_EXCHANGE = "AMQ-RPC"
|
|
AMQ_REPLY_TO_EXCHANGE = "AMQ-REPLY-TO"
|
|
DLQ_EXCHANGE = "DLX"
|
|
|
|
|
|
class RouterBase:
|
|
def __init__(self):
|
|
self.route_database = RouteDatabase()
|
|
self.own_routes: Set[AMQRoute] = set()
|
|
self.route_added_notifier: Callable[[AMQRoute], None] | None = None
|
|
self.route_removed_notifier: Callable[[AMQRoute], None] | None = None
|
|
|
|
def get_routing_key(self, message: AMQMessage) -> str:
|
|
return sanitize_as_rabbitmq_name(f"{message.domain}.{message.path}")
|
|
|
|
def wrap_routes(self) -> str:
|
|
return self.route_database.wrap_routes()
|
|
|
|
def wrap_own_routes(self) -> str:
|
|
return ",".join(str(route) for route in self.own_routes)
|
|
|
|
def unwrap_route_list(self, route_string: str) -> List[AMQRoute]:
|
|
logging.info(" [DBG] RouterMaster.unwrapRoutes, route(s): %s", route_string)
|
|
routes = [AMQRouteFactory.from_string(route_str) for route_str in route_string.split(",")]
|
|
return [route for route in routes if route != NULL_ROUTE]
|
|
|
|
def get_routes(self) -> Set[AMQRoute]:
|
|
return self.route_database.get_routes()
|
|
|
|
def find_route(self, domain: str, path: str) -> AMQRoute | None:
|
|
return self.route_database.find_route(domain, path)
|
|
|
|
def find_route_by_message(self, message: AMQMessage) -> AMQRoute | None:
|
|
return self.route_database.find_route(message.domain, message.path)
|
|
|
|
def add_routes(self, message: str) -> None:
|
|
try:
|
|
amq_route_list = self.unwrap_route_list(message)
|
|
logging.info(" [DBG] RouterMaster.addRoute [%d]: %s", len(amq_route_list), message)
|
|
for route in amq_route_list:
|
|
logging.info(" [*] RouterMaster.addRoute, adding route: %s", route)
|
|
if not route.exchange and not route.queue and not route.key:
|
|
logging.error("Cannot setup AMQ Exchange for '%s', at least one of "
|
|
"Exchange, Queue and/or Key must be present in the routing string. "
|
|
"Expected format is '%s'", route, AMQRoute.FORMAT)
|
|
else:
|
|
if route.exchange != DLQ_EXCHANGE:
|
|
is_new_route = self.route_database.add_route(route)
|
|
if is_new_route and self.route_added_notifier:
|
|
self.route_added_notifier(route)
|
|
except Exception as err:
|
|
logging.error(" [E] add_routes error: %s", err)
|
|
|
|
def set_route_added_notifier(self, callback: Callable[[AMQRoute], None]):
|
|
self.route_added_notifier = callback
|
|
|
|
def remove_routes(self, message: str) -> None:
|
|
try:
|
|
to_remove = [AMQRouteFactory.from_string(route_str) for route_str in message.split(",")]
|
|
removed_count = sum(1 for route in to_remove if self.remove_route(route))
|
|
print(f" [*] RouterMaster.removeRoutes(): {message}, removed={removed_count}")
|
|
except IOError as err:
|
|
print(f" [E] Can't remove route(s): {err}")
|
|
|
|
def remove_route(self, route: AMQRoute) -> bool:
|
|
is_route_removed = self.route_database.remove_route(route)
|
|
if is_route_removed and self.route_removed_notifier:
|
|
self.route_removed_notifier(route)
|
|
return is_route_removed
|
|
|
|
def add_route(self, route: AMQRoute) -> None:
|
|
self.route_database.add_route(route)
|
|
|
|
def add_own_route(self, route: AMQRoute) -> None:
|
|
self.own_routes.add(route)
|