Compare commits

..

No commits in common. "2a90c9b8dd765da08eff9bba910bb87b1c224247" and "f6743076b671098c0aa9cbace43efa504738bc42" have entirely different histories.

3 changed files with 36 additions and 39 deletions

View file

@ -15,8 +15,7 @@
"Bash(python -c \"import sys; data = sys.stdin.buffer.read\\(\\); print\\(data.decode\\(''''utf-8'''', errors=''''replace''''\\)\\)\")", "Bash(python -c \"import sys; data = sys.stdin.buffer.read\\(\\); print\\(data.decode\\(''''utf-8'''', errors=''''replace''''\\)\\)\")",
"Bash(docker compose:*)", "Bash(docker compose:*)",
"Bash(grep -r 8000 /c/Users/Professional/Documents/VSCode/utp_service/backend/ --include=*.py --include=*.sh)", "Bash(grep -r 8000 /c/Users/Professional/Documents/VSCode/utp_service/backend/ --include=*.py --include=*.sh)",
"Bash(grep -n \"rack_type\" /c/Users/Professional/Documents/VSCode/utp_service/backend/alembic/versions/*.py)", "Bash(grep -n \"rack_type\" /c/Users/Professional/Documents/VSCode/utp_service/backend/alembic/versions/*.py)"
"Bash(python3 -c \":*)"
] ]
} }
} }

View file

@ -17,7 +17,7 @@ class PingAction(BaseAction):
start = time.monotonic() start = time.monotonic()
try: try:
proc = await asyncio.create_subprocess_exec( proc = await asyncio.create_subprocess_exec(
"ping", "-c", "4", "-W", "2", device.ip, "ping", "-c", "1", "-W", "2", device.ip,
stdout=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
) )

View file

@ -527,18 +527,12 @@ async def _discover_mikrotik_multi(host: str, port: int, options: list) -> str |
return None return None
async def _fetch_slave_config( async def _check_if_slave_multi(
host: str, port: int, options: list, config_path: str host: str, port: int, options: list, master_ip: str, config_path: str
) -> str | None: ) -> tuple[bool, str, str]:
"""Read config from a slave candidate via SSH (one connection). Returns None on failure.""" """Multi-auth version of _check_if_slave."""
try: try:
return await _ssh_run_multi(host, port, options, f"cat {config_path}", timeout=15) config_text = await _ssh_run_multi(host, port, options, f"cat {config_path}", timeout=15)
except Exception:
return None
def _parse_slave_config(config_text: str, master_ip: str, host: str) -> tuple[bool, str, str]:
"""Check parsed config against one master_ip. No network I/O."""
parser = RawConfigParser(strict=False) parser = RawConfigParser(strict=False)
parser.read_string(config_text) parser.read_string(config_text)
linking_cls = parser.get("plugin:linking", "cls", fallback="") linking_cls = parser.get("plugin:linking", "cls", fallback="")
@ -546,10 +540,11 @@ def _parse_slave_config(config_text: str, master_ip: str, host: str) -> tuple[bo
identifier = parser.get("caps.rack", "identifier", fallback="").strip() identifier = parser.get("caps.rack", "identifier", fallback="").strip()
if "secondary" in linking_cls.lower() and master_ip in linking_uri: if "secondary" in linking_cls.lower() and master_ip in linking_uri:
return True, identifier or host, config_text return True, identifier or host, config_text
except Exception:
pass
return False, "", "" return False, "", ""
async def _open_tunnel(tunnel_host: str, tunnel_port: int, ssh_options: list, target_host: str, target_port: int): 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.""" """Try each ssh_option in order; return open asyncssh connection on first success or raise."""
last_exc: Exception = Exception("No SSH options provided") last_exc: Exception = Exception("No SSH options provided")
@ -1199,21 +1194,24 @@ async def discover_via_ssh(
for candidate_ip in slave_candidates: for candidate_ip in slave_candidates:
logger.info(f"[ssh_discovery] Checking slave candidate {candidate_ip}...") logger.info(f"[ssh_discovery] Checking slave candidate {candidate_ip}...")
await _emit("disc_action", {"message": f"Проверяем слейв-кандидата {candidate_ip}..."}) await _emit("disc_action", {"message": f"Проверяем слейв-кандидата {candidate_ip}..."})
# Check each rack host that could be the master — slave's linking.uri must match
# Fetch config once — then check against all masters without extra SSH connections
slave_config_text = await _fetch_slave_config(candidate_ip, ssh_port, ssh_options, config_path)
is_slave = False is_slave = False
slave_identifier = "" slave_identifier = ""
slave_config_text = ""
master_ip_for_slave = "" master_ip_for_slave = ""
if slave_config_text is None:
logger.info(f"[ssh_discovery] {candidate_ip} — could not read config, skipping")
else:
for _, master_host, _, _ in rack_hosts: for _, master_host, _, _ in rack_hosts:
ok, identifier, _ = _parse_slave_config(slave_config_text, master_host, candidate_ip) ok, identifier, slave_cfg = await _check_if_slave_multi(
host=candidate_ip,
port=ssh_port,
options=ssh_options,
master_ip=master_host,
config_path=config_path,
)
if ok: if ok:
is_slave = True is_slave = True
slave_config_text = slave_cfg
master_ip_for_slave = master_host master_ip_for_slave = master_host
# Derive slave name from master's external_id: "lane-1-1" → "lane-1-1-slave"
master_ext_id = next( master_ext_id = next(
(ext_id for ext_id, host, _, _ in rack_hosts if host == master_host), (ext_id for ext_id, host, _, _ in rack_hosts if host == master_host),
master_host, master_host,