A Python port of the Invisible Internet Project (I2P)
1"""I2PTunnel controller group — manages multiple tunnels from config.
2
3Ported from net.i2p.i2ptunnel.TunnelControllerGroup.
4"""
5
6from __future__ import annotations
7
8import logging
9from pathlib import Path
10
11from i2p_apps.i2ptunnel.config import TunnelConfigParser, TunnelDefinition
12from i2p_apps.i2ptunnel.controller import TunnelController, TunnelState
13from i2p_apps.i2ptunnel.sam_session import SessionFactory
14
15logger = logging.getLogger(__name__)
16
17
18class TunnelControllerGroup:
19 """Manages a group of TunnelControllers loaded from config."""
20
21 def __init__(
22 self,
23 config_path: Path,
24 sam_host: str = "127.0.0.1",
25 sam_port: int = 7656,
26 ) -> None:
27 self._config_path = config_path
28 self._session_factory = SessionFactory(sam_host, sam_port)
29 self._controllers: list[TunnelController] = []
30
31 @property
32 def controllers(self) -> list[TunnelController]:
33 return list(self._controllers)
34
35 def get_controller(self, name: str) -> TunnelController | None:
36 for c in self._controllers:
37 if c.config.name == name:
38 return c
39 return None
40
41 def list_controllers(self) -> list[TunnelController]:
42 return list(self._controllers)
43
44 async def load_and_start(self) -> None:
45 """Load config, create controllers, start startOnLoad tunnels."""
46 tunnels = TunnelConfigParser.load(self._config_path)
47
48 # Also load from config.d/ if it exists
49 config_d = self._config_path.parent / (self._config_path.name + ".d")
50 if config_d.exists():
51 tunnels.extend(TunnelConfigParser.load_dir(config_d))
52
53 for td in tunnels:
54 controller = TunnelController(td, self._session_factory)
55 self._controllers.append(controller)
56
57 # Start tunnels marked startOnLoad
58 for controller in self._controllers:
59 if controller.config.start_on_load:
60 try:
61 await controller.start()
62 except Exception:
63 logger.exception("Failed to start tunnel %r",
64 controller.config.name)
65
66 async def start_all(self) -> None:
67 """Start all tunnels."""
68 for controller in self._controllers:
69 if controller.state == TunnelState.STOPPED:
70 try:
71 await controller.start()
72 except Exception:
73 logger.exception("Failed to start tunnel %r",
74 controller.config.name)
75
76 async def stop_all(self) -> None:
77 """Stop all running tunnels."""
78 for controller in self._controllers:
79 if controller.state == TunnelState.RUNNING:
80 try:
81 await controller.stop()
82 except Exception:
83 logger.exception("Failed to stop tunnel %r",
84 controller.config.name)
85
86 # Release shared sessions
87 await self._session_factory.close_all()