feat: Enhance information displayed on the system status dashboard

This commit is contained in:
Cristi 2026-08-14 23:36:03 +03:00
parent f65d9b17c6
commit a35df76384
4 changed files with 73 additions and 2 deletions

View File

@ -21,7 +21,9 @@ API routes for compute.
import os import os
import psutil import psutil
import cpuinfo
from functools import lru_cache
from gns3server.config import Config from gns3server.config import Config
from gns3server.utils.cpu_percent import CpuPercent from gns3server.utils.cpu_percent import CpuPercent
from gns3server.version import __version__ from gns3server.version import __version__
@ -42,6 +44,11 @@ from typing import Optional, List
router = APIRouter() router = APIRouter()
@lru_cache(maxsize=1)
def get_cpu_model() -> str:
return cpuinfo.get_cpu_info().get("brand_raw", "")
@router.post("/projects/{project_id}/ports/udp", status_code=status.HTTP_201_CREATED) @router.post("/projects/{project_id}/ports/udp", status_code=status.HTTP_201_CREATED)
def allocate_udp_port(project_id: UUID) -> dict: def allocate_udp_port(project_id: UUID) -> dict:
""" """
@ -119,10 +126,15 @@ def compute_statistics() -> dict:
swap_free = psutil.swap_memory().free swap_free = psutil.swap_memory().free
swap_used = psutil.swap_memory().used swap_used = psutil.swap_memory().used
cpu_percent = int(CpuPercent.get()) cpu_percent = int(CpuPercent.get())
load_average_percent = [int(x / psutil.cpu_count() * 100) for x in psutil.getloadavg()] cpu_count = psutil.cpu_count(logical=True) or 1
cpu_count_physical = psutil.cpu_count(logical=False)
raw_load_average = psutil.getloadavg()
load_average = [round(x, 2) for x in raw_load_average]
load_average_percent = [round(x / cpu_count * 100, 2) for x in raw_load_average]
memory_percent = int(psutil.virtual_memory().percent) memory_percent = int(psutil.virtual_memory().percent)
swap_percent = int(psutil.swap_memory().percent) swap_percent = int(psutil.swap_memory().percent)
disk_usage_percent = int(psutil.disk_usage(get_default_project_directory()).percent) disk_usage = psutil.disk_usage(get_default_project_directory())
disk_usage_percent = int(disk_usage.percent)
except psutil.Error as e: except psutil.Error as e:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR)
# raise HTTPConflict(text="Psutil error detected: {}".format(e)) # raise HTTPConflict(text="Psutil error detected: {}".format(e))
@ -135,9 +147,16 @@ def compute_statistics() -> dict:
"swap_free": swap_free, "swap_free": swap_free,
"swap_used": swap_used, "swap_used": swap_used,
"cpu_usage_percent": cpu_percent, "cpu_usage_percent": cpu_percent,
"cpu_count": cpu_count,
"cpu_count_physical": cpu_count_physical,
"cpu_model": get_cpu_model(),
"memory_usage_percent": memory_percent, "memory_usage_percent": memory_percent,
"swap_usage_percent": swap_percent, "swap_usage_percent": swap_percent,
"disk_usage_percent": disk_usage_percent, "disk_usage_percent": disk_usage_percent,
"disk_total": disk_usage.total,
"disk_used": disk_usage.used,
"disk_free": disk_usage.free,
"load_average": load_average,
"load_average_percent": load_average_percent, "load_average_percent": load_average_percent,
} }

View File

@ -17,6 +17,8 @@
import asyncio import asyncio
import signal import signal
import os import os
import time
import psutil
from fastapi import APIRouter, Request, Depends, WebSocket, WebSocketDisconnect, status from fastapi import APIRouter, Request, Depends, WebSocket, WebSocketDisconnect, status
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
@ -42,6 +44,13 @@ log = logging.getLogger(__name__)
router = APIRouter() router = APIRouter()
def get_server_uptime_seconds() -> int:
try:
return max(0, int(time.time() - psutil.Process().create_time()))
except psutil.Error:
return 0
@router.get( @router.get(
"/version", "/version",
response_model=schemas.Version, response_model=schemas.Version,
@ -246,6 +255,7 @@ async def statistics() -> dict:
webwireshark_stats = await collect_webwireshark_stats(projects) webwireshark_stats = await collect_webwireshark_stats(projects)
return { return {
"uptime_seconds": get_server_uptime_seconds(),
"computes": compute_statistics, "computes": compute_statistics,
"projects": project_stats, "projects": project_stats,
"nodes": node_stats, "nodes": node_stats,

View File

@ -0,0 +1,28 @@
from types import SimpleNamespace
from unittest.mock import patch
from gns3server.api.routes.compute.compute import compute_statistics
def test_compute_statistics_preserves_load_average_precision() -> None:
memory = SimpleNamespace(total=8192, available=4096, percent=50)
swap = SimpleNamespace(total=1024, free=512, used=512, percent=50)
disk = SimpleNamespace(total=16384, used=4096, free=12288, percent=25)
with patch("gns3server.api.routes.compute.compute.CpuPercent.get", return_value=10), \
patch("gns3server.api.routes.compute.compute.get_cpu_model", return_value="Test CPU"), \
patch("gns3server.api.routes.compute.compute.psutil.virtual_memory", return_value=memory), \
patch("gns3server.api.routes.compute.compute.psutil.swap_memory", return_value=swap), \
patch("gns3server.api.routes.compute.compute.psutil.disk_usage", return_value=disk), \
patch("gns3server.api.routes.compute.compute.psutil.cpu_count", return_value=4), \
patch("gns3server.api.routes.compute.compute.psutil.getloadavg", return_value=(1.25, 2.5, 3.75)):
statistics = compute_statistics()
assert statistics["load_average"] == [1.25, 2.5, 3.75]
assert statistics["load_average_percent"] == [31.25, 62.5, 93.75]
assert statistics["cpu_count"] == 4
assert statistics["cpu_count_physical"] == 4
assert statistics["cpu_model"] == "Test CPU"
assert statistics["disk_total"] == 16384
assert statistics["disk_used"] == 4096
assert statistics["disk_free"] == 12288

View File

@ -0,0 +1,14 @@
from unittest.mock import MagicMock, patch
from gns3server.api.routes.controller.controller import get_server_uptime_seconds
def test_server_uptime_uses_process_creation_time() -> None:
process = MagicMock()
process.create_time.return_value = 50.25
with patch("gns3server.api.routes.controller.controller.time.time", return_value=200.75), \
patch("gns3server.api.routes.controller.controller.psutil.Process", return_value=process):
uptime = get_server_uptime_seconds()
assert uptime == 150