fix: health history all-down — connectivity checks and UI data path

Service manager fixes (connectivity tests):
- email_manager: replace telnet with socket.create_connection for SMTP/IMAP;
  replace nslookup with socket.getaddrinfo for DNS; exclude unconfigured domain
  from success (email healthy=False now correctly means ports refused, not missing domain)
- calendar_manager: replace localhost:5232 with cell-radicale:5232;
  fix database check to test dir writability instead of file existence (files created on demand)
- file_manager: replace localhost:8080 with cell-webdav:80; add top-level success key
- network_manager: replace nslookup with socket.getaddrinfo;
  add success key to dhcp_test and ntp_test return values
- routing_manager: exclude iptables_access from success
  (iptables runs in cell-wireguard, not API container)
- wireguard_manager: add success key to no-arg test_connectivity result

Health history UI:
- SvcCol reads data?.status?.running || data?.status?.status — handles nested health check shape

Result: network/wireguard/calendar/files/routing/vault all healthy=True.
Email healthy=False is correct — mail server needs ≥1 account before Dovecot starts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-21 02:18:23 -04:00
parent f848a1d056
commit a5381b2ebc
7 changed files with 68 additions and 117 deletions
+12 -19
View File
@@ -292,23 +292,14 @@ class NetworkManager(BaseServiceManager):
logger.error(f"Failed to reload DHCP service: {e}")
def test_dns_resolution(self, domain: str) -> Dict:
"""Test DNS resolution for a domain"""
"""Test DNS resolution for a domain using Python socket."""
import socket
try:
result = subprocess.run(['nslookup', domain, '127.0.0.1'],
capture_output=True, text=True, timeout=10)
return {
'success': result.returncode == 0,
'output': result.stdout,
'error': result.stderr
}
results = socket.getaddrinfo(domain, None)
addrs = [r[4][0] for r in results]
return {'success': True, 'output': f"Resolved: {', '.join(addrs)}", 'error': ''}
except Exception as e:
return {
'success': False,
'output': '',
'error': str(e)
}
return {'success': False, 'output': '', 'error': str(e)}
def test_dhcp_functionality(self) -> Dict:
"""Test DHCP functionality"""
@@ -323,14 +314,15 @@ class NetworkManager(BaseServiceManager):
leases = self.get_dhcp_leases()
return {
'success': is_running,
'running': is_running,
'leases_count': len(leases),
'leases': leases
}
except Exception as e:
logger.error(f"Failed to test DHCP functionality: {e}")
return {'running': False, 'leases_count': 0, 'leases': []}
return {'success': False, 'running': False, 'leases_count': 0, 'leases': []}
def test_ntp_functionality(self) -> Dict:
"""Test NTP functionality"""
@@ -354,13 +346,14 @@ class NetworkManager(BaseServiceManager):
ntp_test['error'] = str(e)
return {
'success': is_running,
'running': is_running,
'ntp_test': ntp_test
}
except Exception as e:
logger.error(f"Failed to test NTP functionality: {e}")
return {'running': False, 'ntp_test': {}}
return {'success': False, 'running': False, 'ntp_test': {}}
def get_network_info(self) -> dict:
"""Return general network info: IP addresses, interfaces, gateway, DNS, etc."""