sts-sponsors team mailing list archive
-
sts-sponsors team
-
Mailing list archive
-
Message #08540
[Merge] ~r00ta/maas-site-manager:MAASENG-1628 into maas-site-manager:main
Jacopo Rota has proposed merging ~r00ta/maas-site-manager:MAASENG-1628 into maas-site-manager:main.
Commit message:
add connection_status in GET sites response
Requested reviews:
MAAS Lander (maas-lander): unittests
MAAS Committers (maas-committers)
For more details, see:
https://code.launchpad.net/~r00ta/maas-site-manager/+git/site-manager/+merge/443281
What's included:
- add `connection_status` property in GET sites response as per https://warthogs.atlassian.net/browse/MAASENG-1628
--
Your team MAAS Committers is requested to review the proposed merge of ~r00ta/maas-site-manager:MAASENG-1628 into maas-site-manager:main.
diff --git a/backend/msm/db/models.py b/backend/msm/db/models.py
index 078946a..edabb41 100644
--- a/backend/msm/db/models.py
+++ b/backend/msm/db/models.py
@@ -1,4 +1,5 @@
from datetime import datetime
+from enum import Enum
from uuid import UUID
from pydantic import (
@@ -11,6 +12,12 @@ from pydantic import (
from ..schema import TimeZone
+class ConnectionStatus(str, Enum):
+ STABLE = "stable"
+ LOST = "lost"
+ UNKNOWN = "unknown"
+
+
class SiteData(BaseModel):
"""Data for a site."""
@@ -38,6 +45,7 @@ class Site(BaseModel):
street: str | None
timezone: TimeZone | None
url: str
+ connection_status: str
stats: SiteData | None
diff --git a/backend/msm/db/queries.py b/backend/msm/db/queries.py
index 77b173e..16df66f 100644
--- a/backend/msm/db/queries.py
+++ b/backend/msm/db/queries.py
@@ -23,6 +23,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.sql.expression import FromClause
from ..schema import MAX_PAGE_SIZE
+from ..settings import SETTINGS
from ._tables import (
Site,
SiteData,
@@ -30,6 +31,7 @@ from ._tables import (
User,
)
from .models import (
+ ConnectionStatus,
PendingSite as PendingSiteSchema,
Site as SiteSchema,
Token as TokenSchema,
@@ -146,6 +148,9 @@ async def get_sites(
)
filters.append(Site.c.accepted == True) # noqa
count = await row_count(session, Site, *filters)
+ connection_lost_timedelta = datetime.utcnow() - timedelta(
+ seconds=SETTINGS.lost_connection_threshold_seconds
+ )
stmt = (
select(
Site.c.id,
@@ -163,6 +168,19 @@ async def get_sites(
case(
(
SiteData.c.site_id != None, # noqa: E711
+ case(
+ (
+ SiteData.c.last_seen > connection_lost_timedelta,
+ ConnectionStatus.STABLE,
+ ),
+ else_=ConnectionStatus.LOST,
+ ),
+ ),
+ else_=ConnectionStatus.UNKNOWN,
+ ).label("connection_status"),
+ case(
+ (
+ SiteData.c.site_id != None, # noqa: E711
func.json_build_object(
"total_machines",
(
diff --git a/backend/msm/settings.py b/backend/msm/settings.py
index a010b54..4bfb651 100644
--- a/backend/msm/settings.py
+++ b/backend/msm/settings.py
@@ -44,5 +44,9 @@ class Settings(BaseSettings):
access_token_expire_minutes = int(getenv("TOKEN_EXPIRATION_TIME", 30))
+ lost_connection_threshold_seconds = int(
+ getenv("LOST_CONNECTION_THRESHOLD", 60)
+ )
+
SETTINGS = Settings()
diff --git a/backend/tests/user_api/test_handlers.py b/backend/tests/user_api/test_handlers.py
index b4ae28c..422a360 100644
--- a/backend/tests/user_api/test_handlers.py
+++ b/backend/tests/user_api/test_handlers.py
@@ -8,6 +8,8 @@ from httpx import AsyncClient
import pytest
from msm import __version__
+from msm.db.models import ConnectionStatus
+from msm.settings import SETTINGS
from ..fixtures.app import AuthAsyncClient
from ..fixtures.db import Fixture
@@ -65,10 +67,10 @@ class TestSitesHandler:
],
)
for site in sites:
+ site["connection_status"] = ConnectionStatus.UNKNOWN
site["stats"] = None
del site["created"]
del site["accepted"]
-
page1 = await authenticated_user_app_client.get("/sites")
assert page1.status_code == 200
assert page1.json() == {
@@ -109,6 +111,7 @@ class TestSitesHandler:
],
)
created_site["stats"] = None
+ created_site["connection_status"] = ConnectionStatus.UNKNOWN
del created_site["created"]
del created_site["accepted"]
@@ -132,6 +135,7 @@ class TestSitesHandler:
],
)
created_site["stats"] = None
+ created_site["connection_status"] = ConnectionStatus.UNKNOWN
del created_site["created"]
del created_site["accepted"]
page1 = await authenticated_user_app_client.get(
@@ -168,6 +172,7 @@ class TestSitesHandler:
site_data["last_seen"] = site_data["last_seen"].isoformat()
site_data["total_machines"] = 105
site["stats"] = site_data
+ site["connection_status"] = ConnectionStatus.STABLE
del site["created"]
del site["accepted"]
@@ -180,6 +185,38 @@ class TestSitesHandler:
"items": [site],
}
+ async def test_get_connection_status(
+ self, authenticated_user_app_client: AuthAsyncClient, fixture: Fixture
+ ) -> None:
+ [site] = await fixture.create("site", [site_details()])
+ await fixture.create(
+ "site_data",
+ [
+ {
+ "site_id": site["id"],
+ "allocated_machines": 10,
+ "deployed_machines": 20,
+ "ready_machines": 30,
+ "error_machines": 40,
+ "other_machines": 5,
+ # Set last_seen to 1 second after the
+ # lost_connection_threshold setting in order to test
+ # that the connection is marked as lost
+ "last_seen": datetime.utcnow()
+ - timedelta(
+ seconds=SETTINGS.lost_connection_threshold_seconds + 1
+ ),
+ }
+ ],
+ )
+
+ page = await authenticated_user_app_client.get("/sites")
+ assert page.status_code == 200
+ assert (
+ page.json()["items"][0]["connection_status"]
+ == ConnectionStatus.LOST
+ )
+
@pytest.mark.asyncio
class TestPendingSitesHandler: