"""NodeManager — daemon application that orchestrates engine and KV-store services."""
from motor.common.app import Application
from motor.common.logger import get_logger
from motor.common.utils.env import Env
from motor.config.node_manager import NodeManagerConfig
from motor.node_manager.api_server.node_manager_api import NodeManagerAPI
from motor.node_manager.core.daemon import Daemon
from motor.node_manager.core.engine_manager import EngineManager
from motor.node_manager.core.heartbeat_manager import HeartbeatManager
logger = get_logger(__name__)
class NodeManager(Application):
"""Orchestrates NodeManager modules and daemon lifecycle.
Module set depends on the active service profile:
* engine active → EngineManager + HeartbeatManager (registration, heartbeats)
* engine absent → only Daemon + NodeManagerAPI (KV-only pod)
"""
def __init__(self, config: NodeManagerConfig) -> None:
role = f"NodeManager.{Env.role}" if Env.role else "NodeManager"
super().__init__(
config,
banner_label=role,
check_interval=config.basic_config.daemon_loop_interval,
)
def init_modules(self) -> None:
daemon = Daemon(self.config)
self.add_module("Daemon", daemon)
self.add_module("NodeManagerAPI", NodeManagerAPI(config=self.config))
if daemon.has_engine:
self.add_module("EngineManager", EngineManager(self.config))
self.add_module("HeartbeatManager", HeartbeatManager(self.config))
logger.info(
"All modules initialized (has_engine=%s, services=%d)",
daemon.has_engine,
len(self.modules),
)
@property
def daemon(self) -> Daemon:
return self.get_module("Daemon")
def _refresh_check_interval(self) -> None:
self._check_interval = self.config.basic_config.daemon_loop_interval
def _start_modules(self) -> None:
self._start_config_watcher()
def _start_config_watcher(self) -> None:
if self.config.snapshot_config.enable_snapshot:
logger.info("[snapshot] Snapshot enabled, configuration file watcher disabled")
return
super()._start_config_watcher()
def _on_daemon_tick(self) -> None:
if self._check_suicide():
logger.error("Detected suicide flag from HeartbeatManager")
self.stop_event.set()
def shutdown(self) -> None:
logger.info("Shutting down NodeManager...")
super().shutdown()
logger.info("NodeManager shutdown complete (exit_code=%d)", self.exit_code)
def _check_suicide(self) -> bool:
"""True when HeartbeatManager requests pod rescheduling."""
hb = self.get_module("HeartbeatManager")
if hb is None:
return False
return hb.should_suicide()