Skip to content

Commit 2d0598c

Browse files
Add resolution center support (#11)
* Add resolution center support * Fix docstring typo Co-authored-by: Martin Hjelmare <[email protected]> --------- Co-authored-by: Martin Hjelmare <[email protected]>
1 parent 7819a28 commit 2d0598c

File tree

7 files changed

+475
-0
lines changed

7 files changed

+475
-0
lines changed

aiohasupervisor/models/__init__.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@
2323
StoreInfo,
2424
SupervisorRole,
2525
)
26+
from aiohasupervisor.models.resolution import (
27+
Check,
28+
CheckOptions,
29+
CheckType,
30+
ContextType,
31+
Issue,
32+
IssueType,
33+
ResolutionInfo,
34+
Suggestion,
35+
SuggestionType,
36+
UnhealthyReason,
37+
UnsupportedReason,
38+
)
2639
from aiohasupervisor.models.root import (
2740
AvailableUpdate,
2841
HostFeature,
@@ -62,4 +75,15 @@
6275
"StoreInfo",
6376
"StoreAddonUpdate",
6477
"StoreAddRepository",
78+
"Check",
79+
"CheckOptions",
80+
"CheckType",
81+
"ContextType",
82+
"Issue",
83+
"IssueType",
84+
"ResolutionInfo",
85+
"Suggestion",
86+
"SuggestionType",
87+
"UnhealthyReason",
88+
"UnsupportedReason",
6589
]
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
"""Models for resolution center APIs."""
2+
3+
from dataclasses import dataclass
4+
from enum import StrEnum
5+
from uuid import UUID
6+
7+
from .base import Options, ResponseData
8+
9+
# --- ENUMS ----
10+
11+
12+
class SuggestionType(StrEnum):
13+
"""SuggestionType type.
14+
15+
This is an incomplete list. Supervisor regularly adds new types of suggestions as
16+
they are discovered. Therefore when returning a suggestion, it may have a type that
17+
is not in this list parsed as strings on older versions of the client.
18+
"""
19+
20+
ADOPT_DATA_DISK = "adopt_data_disk"
21+
CLEAR_FULL_BACKUP = "clear_full_backup"
22+
CREATE_FULL_BACKUP = "create_full_backup"
23+
EXECUTE_INTEGRITY = "execute_integrity"
24+
EXECUTE_REBOOT = "execute_reboot"
25+
EXECUTE_REBUILD = "execute_rebuild"
26+
EXECUTE_RELOAD = "execute_reload"
27+
EXECUTE_REMOVE = "execute_remove"
28+
EXECUTE_REPAIR = "execute_repair"
29+
EXECUTE_RESET = "execute_reset"
30+
EXECUTE_STOP = "execute_stop"
31+
EXECUTE_UPDATE = "execute_update"
32+
REGISTRY_LOGIN = "registry_login"
33+
RENAME_DATA_DISK = "rename_data_disk"
34+
35+
36+
class IssueType(StrEnum):
37+
"""IssueType type.
38+
39+
This is an incomplete list. Supervisor regularly adds new types of issues as they
40+
are discovered. Therefore when returning an issue, it may have a type that is not
41+
in this list parsed as strings on older versions of the client.
42+
"""
43+
44+
CORRUPT_DOCKER = "corrupt_docker"
45+
CORRUPT_REPOSITORY = "corrupt_repository"
46+
CORRUPT_FILESYSTEM = "corrupt_filesystem"
47+
DETACHED_ADDON_MISSING = "detached_addon_missing"
48+
DETACHED_ADDON_REMOVED = "detached_addon_removed"
49+
DISABLED_DATA_DISK = "disabled_data_disk"
50+
DNS_LOOP = "dns_loop"
51+
DNS_SERVER_FAILED = "dns_server_failed"
52+
DNS_SERVER_IPV6_ERROR = "dns_server_ipv6_error"
53+
DOCKER_CONFIG = "docker_config"
54+
DOCKER_RATELIMIT = "docker_ratelimit"
55+
FATAL_ERROR = "fatal_error"
56+
FREE_SPACE = "free_space"
57+
IPV4_CONNECTION_PROBLEM = "ipv4_connection_problem"
58+
MISSING_IMAGE = "missing_image"
59+
MOUNT_FAILED = "mount_failed"
60+
MULTIPLE_DATA_DISKS = "multiple_data_disks"
61+
NO_CURRENT_BACKUP = "no_current_backup"
62+
PWNED = "pwned"
63+
REBOOT_REQUIRED = "reboot_required"
64+
SECURITY = "security"
65+
TRUST = "trust"
66+
UPDATE_FAILED = "update_failed"
67+
UPDATE_ROLLBACK = "update_rollback"
68+
69+
70+
class UnsupportedReason(StrEnum):
71+
"""UnsupportedReason type.
72+
73+
This is an incomplete list. Supervisor regularly adds new unsupported
74+
reasons as they are discovered. Therefore when returning a list of unsupported
75+
reasons, some may not be in this list parsed as strings on older versions of the
76+
client.
77+
"""
78+
79+
APPARMOR = "apparmor"
80+
CGROUP_VERSION = "cgroup_version"
81+
CONNECTIVITY_CHECK = "connectivity_check"
82+
CONTENT_TRUST = "content_trust"
83+
DBUS = "dbus"
84+
DNS_SERVER = "dns_server"
85+
DOCKER_CONFIGURATION = "docker_configuration"
86+
DOCKER_VERSION = "docker_version"
87+
JOB_CONDITIONS = "job_conditions"
88+
LXC = "lxc"
89+
NETWORK_MANAGER = "network_manager"
90+
OS = "os"
91+
OS_AGENT = "os_agent"
92+
PRIVILEGED = "privileged"
93+
RESTART_POLICY = "restart_policy"
94+
SOFTWARE = "software"
95+
SOURCE_MODS = "source_mods"
96+
SUPERVISOR_VERSION = "supervisor_version"
97+
SYSTEMD = "systemd"
98+
SYSTEMD_JOURNAL = "systemd_journal"
99+
SYSTEMD_RESOLVED = "systemd_resolved"
100+
VIRTUALIZATION_IMAGE = "virtualization_image"
101+
102+
103+
class UnhealthyReason(StrEnum):
104+
"""UnhealthyReason type.
105+
106+
This is an incomplete list. Supervisor regularly adds new unhealthy
107+
reasons as they are discovered. Therefore when returning a list of unhealthy
108+
reasons, some may not be in this list parsed as strings on older versions of the
109+
client.
110+
"""
111+
112+
DOCKER = "docker"
113+
OSERROR_BAD_MESSAGE = "oserror_bad_message"
114+
PRIVILEGED = "privileged"
115+
SUPERVISOR = "supervisor"
116+
SETUP = "setup"
117+
UNTRUSTED = "untrusted"
118+
119+
120+
class ContextType(StrEnum):
121+
"""ContextType type."""
122+
123+
ADDON = "addon"
124+
CORE = "core"
125+
DNS_SERVER = "dns_server"
126+
MOUNT = "mount"
127+
OS = "os"
128+
PLUGIN = "plugin"
129+
SUPERVISOR = "supervisor"
130+
STORE = "store"
131+
SYSTEM = "system"
132+
133+
134+
class CheckType(StrEnum):
135+
"""CheckType type.
136+
137+
This is an incomplete list. Supervisor regularly adds new checks as they are
138+
discovered. Therefore when returning a list of checks, some may have a type that is
139+
not in this list parsed as strings on older versions of the client.
140+
"""
141+
142+
ADDON_PWNED = "addon_pwned"
143+
BACKUPS = "backups"
144+
CORE_SECURITY = "core_security"
145+
DETACHED_ADDON_MISSING = "detached_addon_missing"
146+
DETACHED_ADDON_REMOVED = "detached_addon_removed"
147+
DISABLED_DATA_DISK = "disabled_data_disk"
148+
DNS_SERVER_IPV6 = "dns_server_ipv6"
149+
DNS_SERVER = "dns_server"
150+
DOCKER_CONFIG = "docker_config"
151+
FREE_SPACE = "free_space"
152+
MULTIPLE_DATA_DISKS = "multiple_data_disks"
153+
NETWORK_INTERFACE_IPV4 = "network_interface_ipv4"
154+
SUPERVISOR_TRUST = "supervisor_trust"
155+
156+
157+
# --- OBJECTS ----
158+
159+
160+
@dataclass(frozen=True, slots=True)
161+
class Suggestion(ResponseData):
162+
"""Suggestion model."""
163+
164+
type: SuggestionType | str
165+
context: ContextType
166+
reference: str | None
167+
uuid: UUID
168+
auto: bool
169+
170+
171+
@dataclass(frozen=True, slots=True)
172+
class Issue(ResponseData):
173+
"""Issue model."""
174+
175+
type: IssueType | str
176+
context: ContextType
177+
reference: str | None
178+
uuid: UUID
179+
180+
181+
@dataclass(frozen=True, slots=True)
182+
class Check(ResponseData):
183+
"""Check model."""
184+
185+
enabled: bool
186+
slug: CheckType | str
187+
188+
189+
@dataclass(frozen=True, slots=True)
190+
class SuggestionsList(ResponseData):
191+
"""SuggestionsList model."""
192+
193+
suggestions: list[Suggestion]
194+
195+
196+
@dataclass(frozen=True, slots=True)
197+
class ResolutionInfo(SuggestionsList, ResponseData):
198+
"""ResolutionInfo model."""
199+
200+
unsupported: list[UnsupportedReason | str]
201+
unhealthy: list[UnhealthyReason | str]
202+
issues: list[Issue]
203+
checks: list[Check]
204+
205+
206+
@dataclass(frozen=True, slots=True)
207+
class CheckOptions(Options):
208+
"""CheckOptions model."""
209+
210+
enabled: bool | None = None

aiohasupervisor/resolution.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""Resolution center client for supervisor."""
2+
3+
from uuid import UUID
4+
5+
from .client import _SupervisorComponentClient
6+
from .models.resolution import (
7+
CheckOptions,
8+
CheckType,
9+
ResolutionInfo,
10+
Suggestion,
11+
SuggestionsList,
12+
)
13+
14+
15+
class ResolutionClient(_SupervisorComponentClient):
16+
"""Handles resolution center access in supervisor."""
17+
18+
async def info(self) -> ResolutionInfo:
19+
"""Get resolution center info."""
20+
result = await self._client.get("resolution/info")
21+
return ResolutionInfo.from_dict(result.data)
22+
23+
async def check_options(
24+
self, check: CheckType | str, options: CheckOptions
25+
) -> None:
26+
"""Set options for a check."""
27+
await self._client.post(
28+
f"resolution/check/{check}/options", json=options.to_dict()
29+
)
30+
31+
async def run_check(self, check: CheckType | str) -> None:
32+
"""Run a check."""
33+
await self._client.post(f"resolution/check/{check}/run")
34+
35+
async def apply_suggestion(self, suggestion: UUID) -> None:
36+
"""Apply a suggestion."""
37+
await self._client.post(f"resolution/suggestion/{suggestion.hex}")
38+
39+
async def dismiss_suggestion(self, suggestion: UUID) -> None:
40+
"""Dismiss a suggestion."""
41+
await self._client.delete(f"resolution/suggestion/{suggestion.hex}")
42+
43+
async def dismiss_issue(self, issue: UUID) -> None:
44+
"""Dismiss an issue."""
45+
await self._client.delete(f"resolution/issue/{issue.hex}")
46+
47+
async def suggestions_for_issue(self, issue: UUID) -> list[Suggestion]:
48+
"""Get suggestions for issue."""
49+
result = await self._client.get(f"resolution/issue/{issue.hex}/suggestions")
50+
return SuggestionsList.from_dict(result.data).suggestions
51+
52+
async def healthcheck(self) -> None:
53+
"""Run a healthcheck."""
54+
await self._client.post("resolution/healthcheck")

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 .resolution import ResolutionClient
1011
from .store import StoreClient
1112

1213

@@ -23,13 +24,19 @@ def __init__(
2324
"""Initialize client."""
2425
self._client = _SupervisorClient(api_host, token, request_timeout, session)
2526
self._addons = AddonsClient(self._client)
27+
self._resolution = ResolutionClient(self._client)
2628
self._store = StoreClient(self._client)
2729

2830
@property
2931
def addons(self) -> AddonsClient:
3032
"""Get addons component client."""
3133
return self._addons
3234

35+
@property
36+
def resolution(self) -> ResolutionClient:
37+
"""Get resolution center component client."""
38+
return self._resolution
39+
3340
@property
3441
def store(self) -> StoreClient:
3542
"""Get store component client."""
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"result": "ok",
3+
"data": {
4+
"unsupported": [],
5+
"unhealthy": ["supervisor"],
6+
"suggestions": [
7+
{
8+
"type": "create_full_backup",
9+
"context": "system",
10+
"reference": null,
11+
"uuid": "f87d3556f02c4004a47111c072c76fac",
12+
"auto": false
13+
}
14+
],
15+
"issues": [
16+
{
17+
"type": "no_current_backup",
18+
"context": "system",
19+
"reference": null,
20+
"uuid": "7f0eac2b61c9456dab6970507a276c36"
21+
}
22+
],
23+
"checks": [
24+
{ "enabled": true, "slug": "dns_server_ipv6" },
25+
{ "enabled": true, "slug": "disabled_data_disk" },
26+
{ "enabled": true, "slug": "detached_addon_missing" },
27+
{ "enabled": true, "slug": "multiple_data_disks" },
28+
{ "enabled": true, "slug": "backups" },
29+
{ "enabled": true, "slug": "supervisor_trust" },
30+
{ "enabled": true, "slug": "network_interface_ipv4" },
31+
{ "enabled": true, "slug": "dns_server" },
32+
{ "enabled": true, "slug": "free_space" },
33+
{ "enabled": true, "slug": "detached_addon_removed" },
34+
{ "enabled": true, "slug": "docker_config" },
35+
{ "enabled": true, "slug": "addon_pwned" },
36+
{ "enabled": true, "slug": "core_security" }
37+
]
38+
}
39+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"result": "ok",
3+
"data": {
4+
"suggestions": [
5+
{
6+
"type": "create_full_backup",
7+
"context": "system",
8+
"reference": null,
9+
"uuid": "f87d3556f02c4004a47111c072c76fac",
10+
"auto": false
11+
}
12+
]
13+
}
14+
}

0 commit comments

Comments
 (0)