фигня какая-то ты дурной
This commit is contained in:
parent
a774b613c7
commit
e4b993dbf3
2 changed files with 90 additions and 54 deletions
|
|
@ -61,6 +61,7 @@ async def discover_via_ssh(
|
||||||
return result
|
return result
|
||||||
|
|
||||||
ssh_port = obj.ssh_port or 22
|
ssh_port = obj.ssh_port or 22
|
||||||
|
embedded_ssh_ports = _embedded_ssh_ports(obj)
|
||||||
# First option's password is used by single-auth Proxmox scan (legacy).
|
# First option's password is used by single-auth Proxmox scan (legacy).
|
||||||
_first = ssh_options[0]
|
_first = ssh_options[0]
|
||||||
ssh_password = _first.password if _first.type == "password" else ""
|
ssh_password = _first.password if _first.type == "password" else ""
|
||||||
|
|
@ -141,18 +142,18 @@ async def discover_via_ssh(
|
||||||
db, obj, rack_host, config_path
|
db, obj, rack_host, config_path
|
||||||
)
|
)
|
||||||
|
|
||||||
rack_ssh_ok, config_text = await _ssh_fetch_rack_config(
|
rack_ssh_ok, config_text, rack_ssh_port = await _ssh_fetch_rack_config(
|
||||||
rack_host, ssh_port, ssh_options, effective_config_path, rack_name, result, _emit
|
rack_host, embedded_ssh_ports, ssh_options, effective_config_path, rack_name, result, _emit
|
||||||
)
|
)
|
||||||
_rack_ssh_ok = rack_ssh_ok
|
_rack_ssh_ok = rack_ssh_ok
|
||||||
|
|
||||||
await _upsert_rack_device(
|
await _upsert_rack_device(
|
||||||
db, obj, external_id, rack_host, rack_name,
|
db, obj, external_id, rack_host, rack_name,
|
||||||
rack_id_mapping, rack_ssh_ok, now, result
|
rack_id_mapping, rack_ssh_ok, rack_ssh_port, now, result
|
||||||
)
|
)
|
||||||
|
|
||||||
if rack_ssh_ok:
|
if rack_ssh_ok:
|
||||||
neigh_ips = await get_ip_neighbors_multi(rack_host, ssh_port, ssh_options)
|
neigh_ips = await get_ip_neighbors_multi(rack_host, rack_ssh_port or embedded_ssh_ports[0], ssh_options)
|
||||||
neighbor_candidates.update(neigh_ips)
|
neighbor_candidates.update(neigh_ips)
|
||||||
discovered_device_ips.add(rack_host)
|
discovered_device_ips.add(rack_host)
|
||||||
|
|
||||||
|
|
@ -208,7 +209,7 @@ async def discover_via_ssh(
|
||||||
await _emit("disc_action", {"message": "Слейв-стойки: ARP-кандидатов не найдено"})
|
await _emit("disc_action", {"message": "Слейв-стойки: ARP-кандидатов не найдено"})
|
||||||
|
|
||||||
await _discover_slave_racks(
|
await _discover_slave_racks(
|
||||||
db, obj, slave_candidates, rack_hosts, ssh_port, ssh_options,
|
db, obj, slave_candidates, rack_hosts, embedded_ssh_ports, ssh_options,
|
||||||
config_path, all_rack_ips_set, now, result, _emit
|
config_path, all_rack_ips_set, now, result, _emit
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -229,6 +230,15 @@ async def discover_via_ssh(
|
||||||
|
|
||||||
# ── Phase helpers ─────────────────────────────────────────────────────────────
|
# ── Phase helpers ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def _embedded_ssh_ports(obj: Object) -> list[int]:
|
||||||
|
"""Return SSH ports to try for embedded rack PCs."""
|
||||||
|
ports = [22]
|
||||||
|
object_port = obj.ssh_port or 22
|
||||||
|
if object_port not in ports:
|
||||||
|
ports.append(object_port)
|
||||||
|
return ports
|
||||||
|
|
||||||
|
|
||||||
async def _resolve_db_connection(
|
async def _resolve_db_connection(
|
||||||
obj: Object,
|
obj: Object,
|
||||||
ssh_port: int,
|
ssh_port: int,
|
||||||
|
|
@ -475,44 +485,45 @@ async def _resolve_rack_config_path(
|
||||||
|
|
||||||
async def _ssh_fetch_rack_config(
|
async def _ssh_fetch_rack_config(
|
||||||
rack_host: str,
|
rack_host: str,
|
||||||
ssh_port: int,
|
ssh_ports: list[int],
|
||||||
ssh_options: list,
|
ssh_options: list,
|
||||||
config_path: str,
|
config_path: str,
|
||||||
rack_name: str,
|
rack_name: str,
|
||||||
result: DiscoveryResult,
|
result: DiscoveryResult,
|
||||||
_emit,
|
_emit,
|
||||||
) -> tuple[bool, str | None]:
|
) -> tuple[bool, str | None, int | None]:
|
||||||
"""Attempt to SSH into rack_host and read its config file.
|
"""Attempt to SSH into rack_host and read its config file.
|
||||||
|
|
||||||
Returns (ssh_ok, config_text).
|
Returns (ssh_ok, config_text, ssh_port_used).
|
||||||
"""
|
"""
|
||||||
await _emit("disc_action", {"message": f"SSH → {rack_host} ({rack_name})"})
|
await _emit("disc_action", {"message": f"SSH -> {rack_host} ({rack_name})"})
|
||||||
last_exc: Exception | None = None
|
last_exc: Exception | None = None
|
||||||
for attempt in range(1, 3):
|
for ssh_port in ssh_ports:
|
||||||
try:
|
for attempt in range(1, 3):
|
||||||
config_text = await ssh_run_multi(
|
try:
|
||||||
rack_host, ssh_port, ssh_options, f"cat {config_path}", timeout=30
|
config_text = await ssh_run_multi(
|
||||||
)
|
rack_host, ssh_port, ssh_options, f"cat {config_path}", timeout=30
|
||||||
return True, config_text
|
|
||||||
except TimeoutError as exc:
|
|
||||||
last_exc = exc
|
|
||||||
if attempt < 2:
|
|
||||||
logger.warning(
|
|
||||||
"[discovery] Reading rack config from %s timed out; retrying (%d/2)",
|
|
||||||
rack_host, attempt + 1,
|
|
||||||
)
|
)
|
||||||
await _emit("disc_action", {
|
return True, config_text, ssh_port
|
||||||
"message": f"Повторное чтение конфигурации {rack_host} ({rack_name})..."
|
except TimeoutError as exc:
|
||||||
})
|
last_exc = exc
|
||||||
continue
|
if attempt < 2:
|
||||||
break
|
logger.warning(
|
||||||
except Exception as exc:
|
"[discovery] Reading rack config from %s:%d timed out; retrying (%d/2)",
|
||||||
last_exc = exc
|
rack_host, ssh_port, attempt + 1,
|
||||||
break
|
)
|
||||||
|
await _emit("disc_action", {
|
||||||
result.errors.append(f"SSH to {rack_host} failed: {last_exc}")
|
"message": f"Retrying config read {rack_host}:{ssh_port} ({rack_name})..."
|
||||||
return False, None
|
})
|
||||||
|
continue
|
||||||
|
break
|
||||||
|
except Exception as exc:
|
||||||
|
last_exc = exc
|
||||||
|
logger.info("[discovery] SSH to rack %s:%d failed: %s", rack_host, ssh_port, exc)
|
||||||
|
break
|
||||||
|
|
||||||
|
result.errors.append(f"SSH to {rack_host} failed on ports {ssh_ports}: {last_exc}")
|
||||||
|
return False, None, None
|
||||||
|
|
||||||
async def _upsert_rack_device(
|
async def _upsert_rack_device(
|
||||||
db: AsyncSession,
|
db: AsyncSession,
|
||||||
|
|
@ -522,6 +533,7 @@ async def _upsert_rack_device(
|
||||||
rack_name: str,
|
rack_name: str,
|
||||||
rack_id_mapping: dict,
|
rack_id_mapping: dict,
|
||||||
rack_ssh_ok: bool,
|
rack_ssh_ok: bool,
|
||||||
|
rack_ssh_port: int | None,
|
||||||
now: datetime,
|
now: datetime,
|
||||||
result: DiscoveryResult,
|
result: DiscoveryResult,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
@ -531,6 +543,11 @@ async def _upsert_rack_device(
|
||||||
"[discovery] Processing rack %s (%s), ssh_ok=%s",
|
"[discovery] Processing rack %s (%s), ssh_ok=%s",
|
||||||
rack_host, rack_hostname, rack_ssh_ok,
|
rack_host, rack_hostname, rack_ssh_ok,
|
||||||
)
|
)
|
||||||
|
rack_port_override = (
|
||||||
|
rack_ssh_port
|
||||||
|
if rack_ssh_ok and rack_ssh_port and rack_ssh_port != (obj.ssh_port or 22)
|
||||||
|
else None
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
existing = (await db.execute(
|
existing = (await db.execute(
|
||||||
select(Device).where(Device.object_id == obj.id, Device.ip == rack_host)
|
select(Device).where(Device.object_id == obj.id, Device.ip == rack_host)
|
||||||
|
|
@ -543,6 +560,7 @@ async def _upsert_rack_device(
|
||||||
category="embedded", role="other", source="auto_ssh",
|
category="embedded", role="other", source="auto_ssh",
|
||||||
rack_id=rack_id_mapping.get(external_id),
|
rack_id=rack_id_mapping.get(external_id),
|
||||||
status=status, last_seen=now if rack_ssh_ok else None,
|
status=status, last_seen=now if rack_ssh_ok else None,
|
||||||
|
ssh_port_override=rack_port_override,
|
||||||
))
|
))
|
||||||
logger.info("[discovery] %s (%s) - ADDED: status=%s", rack_host, rack_hostname, status)
|
logger.info("[discovery] %s (%s) - ADDED: status=%s", rack_host, rack_hostname, status)
|
||||||
result.created += 1
|
result.created += 1
|
||||||
|
|
@ -559,6 +577,9 @@ async def _upsert_rack_device(
|
||||||
changes.append("status: →online")
|
changes.append("status: →online")
|
||||||
existing.last_seen = now
|
existing.last_seen = now
|
||||||
changes.append("last_seen: updated")
|
changes.append("last_seen: updated")
|
||||||
|
if existing.ssh_port_override != rack_port_override:
|
||||||
|
existing.ssh_port_override = rack_port_override
|
||||||
|
changes.append(f"ssh_port_override: ->{rack_port_override or 'default'}")
|
||||||
if existing.rack_id is None and rack_id_mapping.get(external_id) is not None:
|
if existing.rack_id is None and rack_id_mapping.get(external_id) is not None:
|
||||||
existing.rack_id = rack_id_mapping[external_id]
|
existing.rack_id = rack_id_mapping[external_id]
|
||||||
changes.append(f"rack_id: None→{existing.rack_id}")
|
changes.append(f"rack_id: None→{existing.rack_id}")
|
||||||
|
|
@ -718,7 +739,7 @@ async def _discover_slave_racks(
|
||||||
obj: Object,
|
obj: Object,
|
||||||
slave_candidates: set[str],
|
slave_candidates: set[str],
|
||||||
rack_hosts: list[tuple],
|
rack_hosts: list[tuple],
|
||||||
ssh_port: int,
|
ssh_ports: list[int],
|
||||||
ssh_options: list,
|
ssh_options: list,
|
||||||
config_path: str,
|
config_path: str,
|
||||||
all_rack_ips_set: set[str],
|
all_rack_ips_set: set[str],
|
||||||
|
|
@ -731,7 +752,7 @@ async def _discover_slave_racks(
|
||||||
await _emit("disc_action", {"message": f"Проверяем слейв-кандидата {candidate_ip}..."})
|
await _emit("disc_action", {"message": f"Проверяем слейв-кандидата {candidate_ip}..."})
|
||||||
|
|
||||||
try:
|
try:
|
||||||
slave_config = await fetch_slave_config(candidate_ip, ssh_port, ssh_options, config_path)
|
slave_config, slave_ssh_port = await fetch_slave_config(candidate_ip, ssh_ports, ssh_options, config_path)
|
||||||
is_slave = False
|
is_slave = False
|
||||||
slave_identifier = ""
|
slave_identifier = ""
|
||||||
master_ip_for_slave = ""
|
master_ip_for_slave = ""
|
||||||
|
|
@ -760,7 +781,10 @@ async def _discover_slave_racks(
|
||||||
|
|
||||||
slave_rack_id = await _ensure_slave_rack(db, obj, slave_identifier, result)
|
slave_rack_id = await _ensure_slave_rack(db, obj, slave_identifier, result)
|
||||||
|
|
||||||
await _upsert_slave_device(db, obj, candidate_ip, slave_identifier, slave_rack_id, master_ip_for_slave, now, result)
|
await _upsert_slave_device(
|
||||||
|
db, obj, candidate_ip, slave_identifier, slave_rack_id,
|
||||||
|
master_ip_for_slave, slave_ssh_port, now, result
|
||||||
|
)
|
||||||
|
|
||||||
all_rack_ips_with_slave = list(all_rack_ips_set) + [candidate_ip]
|
all_rack_ips_with_slave = list(all_rack_ips_set) + [candidate_ip]
|
||||||
slave_devices = parse_config(slave_config, candidate_ip, all_rack_ips_with_slave, obj.server_ip)
|
slave_devices = parse_config(slave_config, candidate_ip, all_rack_ips_with_slave, obj.server_ip)
|
||||||
|
|
@ -820,9 +844,15 @@ async def _upsert_slave_device(
|
||||||
slave_identifier: str,
|
slave_identifier: str,
|
||||||
slave_rack_id: int | None,
|
slave_rack_id: int | None,
|
||||||
master_ip: str,
|
master_ip: str,
|
||||||
|
slave_ssh_port: int | None,
|
||||||
now: datetime,
|
now: datetime,
|
||||||
result: DiscoveryResult,
|
result: DiscoveryResult,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
slave_port_override = (
|
||||||
|
slave_ssh_port
|
||||||
|
if slave_ssh_port and slave_ssh_port != (obj.ssh_port or 22)
|
||||||
|
else None
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
existing = (await db.execute(
|
existing = (await db.execute(
|
||||||
select(Device).where(Device.object_id == obj.id, Device.ip == candidate_ip)
|
select(Device).where(Device.object_id == obj.id, Device.ip == candidate_ip)
|
||||||
|
|
@ -832,6 +862,7 @@ async def _upsert_slave_device(
|
||||||
object_id=obj.id, ip=candidate_ip, hostname=slave_identifier,
|
object_id=obj.id, ip=candidate_ip, hostname=slave_identifier,
|
||||||
category="embedded", role="other", source="auto_ssh",
|
category="embedded", role="other", source="auto_ssh",
|
||||||
rack_id=slave_rack_id, status="online", last_seen=now,
|
rack_id=slave_rack_id, status="online", last_seen=now,
|
||||||
|
ssh_port_override=slave_port_override,
|
||||||
))
|
))
|
||||||
logger.info("[discovery] %s (%s) - ADDED: slave rack PC", candidate_ip, slave_identifier)
|
logger.info("[discovery] %s (%s) - ADDED: slave rack PC", candidate_ip, slave_identifier)
|
||||||
result.created += 1
|
result.created += 1
|
||||||
|
|
@ -847,6 +878,9 @@ async def _upsert_slave_device(
|
||||||
changes.append("status: →online")
|
changes.append("status: →online")
|
||||||
existing.last_seen = now
|
existing.last_seen = now
|
||||||
changes.append("last_seen: updated")
|
changes.append("last_seen: updated")
|
||||||
|
if existing.ssh_port_override != slave_port_override:
|
||||||
|
existing.ssh_port_override = slave_port_override
|
||||||
|
changes.append(f"ssh_port_override: ->{slave_port_override or 'default'}")
|
||||||
if existing.rack_id is None and slave_rack_id:
|
if existing.rack_id is None and slave_rack_id:
|
||||||
existing.rack_id = slave_rack_id
|
existing.rack_id = slave_rack_id
|
||||||
changes.append(f"rack_id: →{slave_rack_id}")
|
changes.append(f"rack_id: →{slave_rack_id}")
|
||||||
|
|
|
||||||
|
|
@ -138,25 +138,27 @@ async def discover_mikrotik_multi(host: str, port: int, options: list) -> str |
|
||||||
|
|
||||||
|
|
||||||
async def fetch_slave_config(
|
async def fetch_slave_config(
|
||||||
host: str, port: int, options: list, config_path: str,
|
host: str, ports: list[int], options: list, config_path: str,
|
||||||
) -> str | None:
|
) -> tuple[str | None, int | None]:
|
||||||
"""Read a remote config file from a slave candidate. Returns None on failure."""
|
"""Read a remote config file from a slave candidate. Returns (config, port)."""
|
||||||
for attempt in range(1, 3):
|
for port in ports:
|
||||||
try:
|
for attempt in range(1, 3):
|
||||||
return await ssh_run_multi(host, port, options, f"cat {config_path}", timeout=30)
|
try:
|
||||||
except TimeoutError as exc:
|
config_text = await ssh_run_multi(host, port, options, f"cat {config_path}", timeout=30)
|
||||||
if attempt < 2:
|
return config_text, port
|
||||||
logger.warning(
|
except TimeoutError as exc:
|
||||||
"[discovery] Reading slave config from %s timed out; retrying (%d/2)",
|
if attempt < 2:
|
||||||
host, attempt + 1,
|
logger.warning(
|
||||||
)
|
"[discovery] Reading slave config from %s:%d timed out; retrying (%d/2)",
|
||||||
continue
|
host, port, attempt + 1,
|
||||||
logger.warning("[discovery] Reading slave config from %s failed: %s", host, exc)
|
)
|
||||||
return None
|
continue
|
||||||
except Exception as exc:
|
logger.warning("[discovery] Reading slave config from %s:%d failed: %s", host, port, exc)
|
||||||
logger.warning("[discovery] Reading slave config from %s failed: %s", host, exc)
|
break
|
||||||
return None
|
except Exception as exc:
|
||||||
return None
|
logger.warning("[discovery] Reading slave config from %s:%d failed: %s", host, port, exc)
|
||||||
|
break
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
async def discover_proxmox_via_port_scan(
|
async def discover_proxmox_via_port_scan(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue