Skip to content

Commit ae2af2f

Browse files
committed
Add Supervisor OS APIs
1 parent 2d0598c commit ae2af2f

File tree

8 files changed

+417
-0
lines changed

8 files changed

+417
-0
lines changed

aiohasupervisor/models/os.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
"""Models for OS APIs."""
2+
3+
from dataclasses import dataclass
4+
from enum import StrEnum
5+
6+
from .base import Options, Request, ResponseData
7+
8+
# --- ENUMS ----
9+
10+
11+
class RaucState(StrEnum):
12+
"""RaucState type."""
13+
14+
GOOD = "good"
15+
BAD = "bad"
16+
ACTIVE = "active"
17+
18+
19+
class BootSlotName(StrEnum):
20+
"""BootSlotName type."""
21+
22+
A = "A"
23+
B = "B"
24+
25+
26+
# --- OBJECTS ----
27+
28+
29+
@dataclass(frozen=True, slots=True)
30+
class BootSlot(ResponseData):
31+
"""BootSlot model."""
32+
33+
state: str
34+
status: RaucState | None
35+
version: str | None
36+
37+
38+
@dataclass(frozen=True, slots=True)
39+
class OSInfo(ResponseData):
40+
"""OSInfo model."""
41+
42+
version: str | None
43+
version_latest: str | None
44+
update_available: bool
45+
board: str | None
46+
boot: str | None
47+
data_disk: str | None
48+
boot_slots: dict[str, BootSlot]
49+
50+
51+
@dataclass(frozen=True, slots=True)
52+
class OSUpdate(Request):
53+
"""OSUpdate model."""
54+
55+
version: str | None = None
56+
57+
58+
@dataclass(frozen=True, slots=True)
59+
class MigrateDataOptions(Request):
60+
"""MigrateDataOptions model."""
61+
62+
device: str
63+
64+
65+
@dataclass(frozen=True, slots=True)
66+
class DataDisk(ResponseData):
67+
"""DataDisk model."""
68+
69+
name: str
70+
vendor: str
71+
model: str
72+
serial: str
73+
size: int
74+
id: str
75+
dev_path: str
76+
77+
78+
@dataclass(frozen=True, slots=True)
79+
class DataDiskList(ResponseData):
80+
"""ListDataDisks model."""
81+
82+
devices: list[str]
83+
disks: list[DataDisk]
84+
85+
86+
@dataclass(frozen=True, slots=True)
87+
class SetBootSlotOptions(Request):
88+
"""SetBootSlotOptions model."""
89+
90+
boot_slot: BootSlotName
91+
92+
93+
@dataclass(frozen=True, slots=True)
94+
class GreenInfo(ResponseData):
95+
"""GreenInfo model."""
96+
97+
activity_led: bool
98+
power_led: bool
99+
system_health_led: bool
100+
101+
102+
@dataclass(frozen=True, slots=True)
103+
class GreenOptions(Options):
104+
"""GreenOptions model."""
105+
106+
activity_led: bool | None = None
107+
power_led: bool | None = None
108+
system_health_led: bool | None = None
109+
110+
111+
@dataclass(frozen=True, slots=True)
112+
class YellowInfo(ResponseData):
113+
"""YellowInfo model."""
114+
115+
disk_led: bool
116+
heartbeat_led: bool
117+
power_led: bool
118+
119+
120+
@dataclass(frozen=True, slots=True)
121+
class YellowOptions(Options):
122+
"""YellowOptions model."""
123+
124+
disk_led: bool | None = None
125+
heartbeat_led: bool | None = None
126+
power_led: bool | None = None

aiohasupervisor/os.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""OS client for supervisor."""
2+
3+
from .client import _SupervisorComponentClient
4+
from .models.os import (
5+
DataDiskList,
6+
GreenInfo,
7+
GreenOptions,
8+
MigrateDataOptions,
9+
OSInfo,
10+
OSUpdate,
11+
SetBootSlotOptions,
12+
YellowInfo,
13+
YellowOptions,
14+
)
15+
16+
17+
class OSClient(_SupervisorComponentClient):
18+
"""Handles OS access in supervisor."""
19+
20+
async def info(self) -> OSInfo:
21+
"""Get OS info."""
22+
result = await self._client.get("os/info")
23+
return OSInfo.from_dict(result.data)
24+
25+
async def update(self, options: OSUpdate | None = None) -> None:
26+
"""Update OS."""
27+
await self._client.post(
28+
"os/update", json=options.to_dict() if options else None
29+
)
30+
31+
async def config_sync(self) -> None:
32+
"""Trigger config reload on OS."""
33+
await self._client.post("os/config/sync")
34+
35+
async def migrate_data(self, options: MigrateDataOptions) -> None:
36+
"""Migrate data to new data disk and reboot."""
37+
await self._client.post("os/datadisk/move", json=options.to_dict())
38+
39+
async def list_data_disks(self) -> DataDiskList:
40+
"""Get all data disks."""
41+
result = await self._client.get("os/datadisk/list")
42+
return DataDiskList.from_dict(result.data)
43+
44+
async def wipe_data(self) -> None:
45+
"""Trigger data disk wipe on host and reboot."""
46+
await self._client.post("os/datadisk/wipe")
47+
48+
async def set_boot_slot(self, options: SetBootSlotOptions) -> None:
49+
"""Change active boot slot on host and reboot."""
50+
await self._client.post("os/boot-slot", json=options.to_dict())
51+
52+
async def green_info(self) -> GreenInfo:
53+
"""Get info for green board (if in use)."""
54+
result = await self._client.get("os/boards/green")
55+
return GreenInfo.from_dict(result.data)
56+
57+
async def green_options(self, options: GreenOptions) -> None:
58+
"""Set options for green board (if in use)."""
59+
await self._client.post("os/boards/green", json=options.to_dict())
60+
61+
async def yellow_info(self) -> YellowInfo:
62+
"""Get info for yellow board (if in use)."""
63+
result = await self._client.get("os/boards/yellow")
64+
return YellowInfo.from_dict(result.data)
65+
66+
async def yellow_options(self, options: YellowOptions) -> None:
67+
"""Set options for yellow board (if in use)."""
68+
await self._client.post("os/boards/yellow", json=options.to_dict())

aiohasupervisor/root.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .addons import AddonsClient
88
from .client import _SupervisorClient
99
from .models.root import AvailableUpdate, AvailableUpdates, RootInfo
10+
from .os import OSClient
1011
from .resolution import ResolutionClient
1112
from .store import StoreClient
1213

@@ -24,6 +25,7 @@ def __init__(
2425
"""Initialize client."""
2526
self._client = _SupervisorClient(api_host, token, request_timeout, session)
2627
self._addons = AddonsClient(self._client)
28+
self._os = OSClient(self._client)
2729
self._resolution = ResolutionClient(self._client)
2830
self._store = StoreClient(self._client)
2931

@@ -32,6 +34,11 @@ def addons(self) -> AddonsClient:
3234
"""Get addons component client."""
3335
return self._addons
3436

37+
@property
38+
def os(self) -> OSClient:
39+
"""Get OS component client."""
40+
return self._os
41+
3542
@property
3643
def resolution(self) -> ResolutionClient:
3744
"""Get resolution center component client."""
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"result": "ok",
3+
"data": {
4+
"devices": ["SSK-SSK-Storage-DF123"],
5+
"disks": [
6+
{
7+
"name": "SSK SSK Storage (DF123)",
8+
"vendor": "SSK",
9+
"model": "SSK Storage",
10+
"serial": "DF123",
11+
"size": 250059350016,
12+
"id": "SSK-SSK-Storage-DF123",
13+
"dev_path": "/dev/sda"
14+
}
15+
]
16+
}
17+
}

tests/fixtures/os_green_info.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"result": "ok",
3+
"data": { "activity_led": true, "power_led": true, "system_health_led": true }
4+
}

tests/fixtures/os_info.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"result": "ok",
3+
"data": {
4+
"version": "13.0",
5+
"version_latest": "13.1",
6+
"update_available": true,
7+
"board": "odroid-n2",
8+
"boot": "B",
9+
"data_disk": "BJTD4R-0xaabbccdd",
10+
"boot_slots": {
11+
"A": { "state": "inactive", "status": "good", "version": null },
12+
"B": {
13+
"state": "booted",
14+
"status": "good",
15+
"version": "13.0"
16+
}
17+
}
18+
}
19+
}

tests/fixtures/os_yellow_info.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"result": "ok",
3+
"data": { "disk_led": true, "heartbeat_led": true, "power_led": true }
4+
}

0 commit comments

Comments
 (0)