фикс ссх дб 3

This commit is contained in:
dv 2026-04-10 14:57:26 +03:00
parent dc677888bb
commit f6743076b6
2 changed files with 41 additions and 18 deletions

View file

@ -545,6 +545,32 @@ async def _check_if_slave_multi(
return False, "", "" return False, "", ""
async def _open_tunnel(tunnel_host: str, tunnel_port: int, ssh_options: list, target_host: str, target_port: int):
"""Try each ssh_option in order; return open asyncssh connection on first success or raise."""
last_exc: Exception = Exception("No SSH options provided")
for opt in ssh_options:
try:
connect_kwargs: dict = dict(
host=tunnel_host, port=tunnel_port, username=opt.username,
known_hosts=None, connect_timeout=10,
)
if opt.type == "key":
connect_kwargs["client_keys"] = [opt.key_path]
connect_kwargs["passphrase"] = None
logger.info("[ssh_discovery] Tunnel attempt for %s — key: user=%s key_path=%s", tunnel_host, opt.username, opt.key_path)
else:
connect_kwargs["password"] = opt.password
masked = (opt.password[:1] + "***" + opt.password[-1:]) if len(opt.password) > 2 else "***"
logger.info("[ssh_discovery] Tunnel attempt for %s — password: user=%s password=%s", tunnel_host, opt.username, masked)
conn = await asyncssh.connect(**connect_kwargs)
logger.info("[ssh_discovery] Tunnel auth succeeded for %s with %s(%s)", tunnel_host, opt.type, opt.username)
return conn
except Exception as exc:
last_exc = exc
logger.info("[ssh_discovery] Tunnel auth failed for %s with %s(%s): %s", tunnel_host, opt.type, opt.username, exc)
raise last_exc
async def _get_rack_hosts_direct( async def _get_rack_hosts_direct(
host: str, host: str,
port: int, port: int,
@ -555,19 +581,13 @@ async def _get_rack_hosts_direct(
via_tunnel: bool = False, via_tunnel: bool = False,
tunnel_host: str | None = None, tunnel_host: str | None = None,
tunnel_ssh_port: int = 22, tunnel_ssh_port: int = 22,
tunnel_ssh_user: str | None = None, ssh_options: list | None = None,
tunnel_ssh_password: str | None = None,
) -> list[tuple[str, str, str, str | None]]: ) -> list[tuple[str, str, str, str | None]]:
"""Return list of (external_id, host_ip, rack_name, rack_type) from the racks table.""" """Return list of (external_id, host_ip, rack_name, rack_type) from the racks table."""
if via_tunnel and tunnel_host and tunnel_ssh_user and tunnel_ssh_password: if via_tunnel and tunnel_host and ssh_options:
async with asyncssh.connect( conn = await _open_tunnel(tunnel_host, tunnel_ssh_port, ssh_options, host, port)
tunnel_host, async with conn:
port=tunnel_ssh_port, listener = await conn.forward_local_port("127.0.0.1", 0, host, port)
username=tunnel_ssh_user,
password=tunnel_ssh_password,
known_hosts=None,
) as tunnel:
listener = await tunnel.forward_local_port("127.0.0.1", 0, host, port)
local_port = listener.get_port() local_port = listener.get_port()
dsn = ( dsn = (
f"host=127.0.0.1 port={local_port} dbname={dbname} " f"host=127.0.0.1 port={local_port} dbname={dbname} "
@ -729,6 +749,7 @@ async def discover_via_ssh(
source="auto_ssh", source="auto_ssh",
location="server_room", location="server_room",
status="unknown", status="unknown",
device_meta={"is_caps_db": True},
) )
db.add(db_device_obj) db.add(db_device_obj)
await db.flush() await db.flush()
@ -764,8 +785,7 @@ async def discover_via_ssh(
via_tunnel=use_tunnel, via_tunnel=use_tunnel,
tunnel_host=obj.server_ip, tunnel_host=obj.server_ip,
tunnel_ssh_port=ssh_port, tunnel_ssh_port=ssh_port,
tunnel_ssh_user=obj.ssh_user, ssh_options=ssh_options,
tunnel_ssh_password=ssh_password,
) )
# Connection succeeded — mark DB device online # Connection succeeded — mark DB device online
if db_device_obj is not None: if db_device_obj is not None:

View file

@ -219,11 +219,14 @@ export function DeviceEditModal({ open, device, objId, onClose, onSaved }: Props
/> />
</Form.Item> </Form.Item>
{device?.device_meta?.is_caps_db && (
<>
<Divider orientation="left" plain>Подключение к БД</Divider> <Divider orientation="left" plain>Подключение к БД</Divider>
<Form.Item name="db_via_tunnel" valuePropName="checked"> <Form.Item name="db_via_tunnel" valuePropName="checked">
<Checkbox>Подключаться к БД через SSH-туннель (через main_server объекта)</Checkbox> <Checkbox>Подключаться к БД через SSH-туннель (через main_server объекта)</Checkbox>
</Form.Item> </Form.Item>
</>
)}
</Form> </Form>
</Modal> </Modal>
) )