diff --git a/backend/app/services/device_discovery.py b/backend/app/services/device_discovery.py index 75ab508..c2c1604 100644 --- a/backend/app/services/device_discovery.py +++ b/backend/app/services/device_discovery.py @@ -13,6 +13,7 @@ Limitations (devices not discoverable automatically): import logging import re +from datetime import datetime, timezone from configparser import RawConfigParser from dataclasses import dataclass, field from io import StringIO @@ -443,6 +444,7 @@ async def discover_via_ssh( db_user: str | None = obj.db_user db_password: str | None = None db_table: str | None = obj.db_table + server_ip_ssh_verified = False if db_host and db_name and db_user and obj.db_pass_enc and db_table: try: @@ -490,6 +492,7 @@ async def discover_via_ssh( db_password = detected["password"] db_name = detected["dbname"] db_table = db_table or obj.db_table or "racks" + server_ip_ssh_verified = True else: result.errors.append( "DB connection not configured. Either fill in the DB fields on the object " @@ -539,13 +542,15 @@ async def discover_via_ssh( logger.info(f"[ssh_discovery] Created rack entry '{ext_id}' (id={new_rack.id})") # Step 1b: Add main CAPS server and DB server as devices + # verified=True means we successfully connected to this host during this discovery run + now = datetime.now(timezone.utc) infra_devices = [] if obj.server_ip: - infra_devices.append((obj.server_ip, "main_server", "caps_server")) + infra_devices.append((obj.server_ip, "main_server", "caps_server", server_ip_ssh_verified)) if db_host and db_host != obj.server_ip: - infra_devices.append((db_host, "vm", "db_server")) + infra_devices.append((db_host, "vm", "db_server", True)) # DB query succeeded = online - for infra_ip, infra_category, infra_hostname in infra_devices: + for infra_ip, infra_category, infra_hostname, infra_verified in infra_devices: try: existing_infra = (await db.execute( select(Device).where( @@ -562,8 +567,10 @@ async def discover_via_ssh( role="other", source="auto_ssh", location="server_room", + status="online" if infra_verified else "unknown", + last_seen=now if infra_verified else None, )) - logger.info(f"[ssh_discovery] {infra_ip} ({infra_hostname}) - ADDED: category={infra_category}, location=server_room, source=auto_ssh") + logger.info(f"[ssh_discovery] {infra_ip} ({infra_hostname}) - ADDED: category={infra_category}, location=server_room, status={'online' if infra_verified else 'unknown'}, source=auto_ssh") result.created += 1 result.actions.append(DeviceAction( ip=infra_ip, @@ -574,16 +581,35 @@ async def discover_via_ssh( role="other", )) else: - logger.info(f"[ssh_discovery] {infra_ip} ({infra_hostname}) - SKIPPED: already exists (category={existing_infra.category}, hostname={existing_infra.hostname})") - result.skipped += 1 - result.actions.append(DeviceAction( - ip=infra_ip, - hostname=existing_infra.hostname or infra_hostname, - action="SKIPPED", - reason=f"already exists (category={existing_infra.category})", - category=existing_infra.category, - role=existing_infra.role, - )) + infra_changes = [] + if infra_verified: + if existing_infra.status != "online": + existing_infra.status = "online" + infra_changes.append("status: →online") + existing_infra.last_seen = now + infra_changes.append(f"last_seen: →{now.isoformat()}") + if infra_changes: + logger.info(f"[ssh_discovery] {infra_ip} ({infra_hostname}) - UPDATED: {', '.join(infra_changes)}") + result.updated += 1 + result.actions.append(DeviceAction( + ip=infra_ip, + hostname=existing_infra.hostname or infra_hostname, + action="UPDATED", + reason="; ".join(infra_changes), + category=existing_infra.category, + role=existing_infra.role, + )) + else: + logger.info(f"[ssh_discovery] {infra_ip} ({infra_hostname}) - SKIPPED: already exists (category={existing_infra.category}, hostname={existing_infra.hostname})") + result.skipped += 1 + result.actions.append(DeviceAction( + ip=infra_ip, + hostname=existing_infra.hostname or infra_hostname, + action="SKIPPED", + reason=f"already exists (category={existing_infra.category})", + category=existing_infra.category, + role=existing_infra.role, + )) except Exception as exc: logger.error(f"[ssh_discovery] {infra_ip} ({infra_hostname}) - ERROR: {exc}") result.errors.append(f"Error upserting infra device {infra_ip}: {exc}") @@ -639,19 +665,27 @@ async def discover_via_ssh( role="other", source="auto_ssh", rack_id=rack_id_mapping.get(external_id), + status="online", + last_seen=now, )) - logger.info(f"[ssh_discovery] {rack_host} ({rack_hostname}) - ADDED: category=main_server, role=other, hostname={rack_hostname}, source=auto_ssh") + logger.info(f"[ssh_discovery] {rack_host} ({rack_hostname}) - ADDED: category=embedded, role=other, hostname={rack_hostname}, status=online, source=auto_ssh") result.created += 1 result.actions.append(DeviceAction( ip=rack_host, hostname=rack_hostname, action="ADDED", - reason="rack main server discovered via SSH connection", + reason="rack PC discovered via SSH connection", category="embedded", role="other", )) else: rack_changes = [] + # SSH succeeded → always update status and last_seen + if existing_rack.status != "online": + existing_rack.status = "online" + rack_changes.append("status: →online") + existing_rack.last_seen = now + rack_changes.append(f"last_seen: →{now.isoformat()}") if existing_rack.rack_id is None and rack_id_mapping.get(external_id) is not None: existing_rack.rack_id = rack_id_mapping[external_id] rack_changes.append(f"rack_id: None→{existing_rack.rack_id}")