mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-19 15:16:33 +00:00
Events when unhealthy/unsupported changes (#3951)
* Events when unhealthy/unsupported changes * called_once_with to called_once
This commit is contained in:
parent
bde5c938a7
commit
5d2b5bada7
@ -32,6 +32,8 @@ class WSEvent(str, Enum):
|
|||||||
"""Websocket events."""
|
"""Websocket events."""
|
||||||
|
|
||||||
ADDON = "addon"
|
ADDON = "addon"
|
||||||
|
HEALTH_CHANGED = "health_changed"
|
||||||
ISSUE_CHANGED = "issue_changed"
|
ISSUE_CHANGED = "issue_changed"
|
||||||
ISSUE_REMOVED = "issue_removed"
|
ISSUE_REMOVED = "issue_removed"
|
||||||
SUPERVISOR_UPDATE = "supervisor_update"
|
SUPERVISOR_UPDATE = "supervisor_update"
|
||||||
|
SUPPORTED_CHANGED = "supported_changed"
|
||||||
|
@ -3,7 +3,13 @@ from uuid import UUID, uuid4
|
|||||||
|
|
||||||
import attr
|
import attr
|
||||||
|
|
||||||
from .const import ContextType, IssueType, SuggestionType
|
from .const import (
|
||||||
|
ContextType,
|
||||||
|
IssueType,
|
||||||
|
SuggestionType,
|
||||||
|
UnhealthyReason,
|
||||||
|
UnsupportedReason,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@attr.s(frozen=True, slots=True)
|
@attr.s(frozen=True, slots=True)
|
||||||
@ -24,3 +30,19 @@ class Suggestion:
|
|||||||
context: ContextType = attr.ib()
|
context: ContextType = attr.ib()
|
||||||
reference: str | None = attr.ib(default=None)
|
reference: str | None = attr.ib(default=None)
|
||||||
uuid: UUID = attr.ib(factory=lambda: uuid4().hex, eq=False, init=False)
|
uuid: UUID = attr.ib(factory=lambda: uuid4().hex, eq=False, init=False)
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(frozen=True, slots=True)
|
||||||
|
class HealthChanged:
|
||||||
|
"""Describe change in system health."""
|
||||||
|
|
||||||
|
healthy: bool = attr.ib()
|
||||||
|
unhealthy_reasons: list[UnhealthyReason] | None = attr.ib(default=None)
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(frozen=True, slots=True)
|
||||||
|
class SupportedChanged:
|
||||||
|
"""Describe change in system supported."""
|
||||||
|
|
||||||
|
supported: bool = attr.ib()
|
||||||
|
unsupported_reasons: list[UnsupportedReason] | None = attr.ib(default=None)
|
||||||
|
@ -18,7 +18,7 @@ from .const import (
|
|||||||
UnhealthyReason,
|
UnhealthyReason,
|
||||||
UnsupportedReason,
|
UnsupportedReason,
|
||||||
)
|
)
|
||||||
from .data import Issue, Suggestion
|
from .data import HealthChanged, Issue, Suggestion, SupportedChanged
|
||||||
from .evaluate import ResolutionEvaluation
|
from .evaluate import ResolutionEvaluation
|
||||||
from .fixup import ResolutionFixup
|
from .fixup import ResolutionFixup
|
||||||
from .notify import ResolutionNotify
|
from .notify import ResolutionNotify
|
||||||
@ -125,6 +125,10 @@ class ResolutionManager(FileConfiguration, CoreSysAttributes):
|
|||||||
"""Add a reason for unsupported."""
|
"""Add a reason for unsupported."""
|
||||||
if reason not in self._unsupported:
|
if reason not in self._unsupported:
|
||||||
self._unsupported.append(reason)
|
self._unsupported.append(reason)
|
||||||
|
self.sys_homeassistant.websocket.supervisor_event(
|
||||||
|
WSEvent.SUPPORTED_CHANGED,
|
||||||
|
attr.asdict(SupportedChanged(False, self.unsupported)),
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unhealthy(self) -> list[UnhealthyReason]:
|
def unhealthy(self) -> list[UnhealthyReason]:
|
||||||
@ -136,6 +140,10 @@ class ResolutionManager(FileConfiguration, CoreSysAttributes):
|
|||||||
"""Add a reason for unsupported."""
|
"""Add a reason for unsupported."""
|
||||||
if reason not in self._unhealthy:
|
if reason not in self._unhealthy:
|
||||||
self._unhealthy.append(reason)
|
self._unhealthy.append(reason)
|
||||||
|
self.sys_homeassistant.websocket.supervisor_event(
|
||||||
|
WSEvent.HEALTH_CHANGED,
|
||||||
|
attr.asdict(HealthChanged(False, self.unhealthy)),
|
||||||
|
)
|
||||||
|
|
||||||
def get_suggestion(self, uuid: str) -> Suggestion:
|
def get_suggestion(self, uuid: str) -> Suggestion:
|
||||||
"""Return suggestion with uuid."""
|
"""Return suggestion with uuid."""
|
||||||
@ -228,6 +236,12 @@ class ResolutionManager(FileConfiguration, CoreSysAttributes):
|
|||||||
if reason not in self._unsupported:
|
if reason not in self._unsupported:
|
||||||
raise ResolutionError(f"The reason {reason} is not active", _LOGGER.warning)
|
raise ResolutionError(f"The reason {reason} is not active", _LOGGER.warning)
|
||||||
self._unsupported.remove(reason)
|
self._unsupported.remove(reason)
|
||||||
|
self.sys_homeassistant.websocket.supervisor_event(
|
||||||
|
WSEvent.SUPPORTED_CHANGED,
|
||||||
|
attr.asdict(
|
||||||
|
SupportedChanged(self.sys_core.supported, self.unsupported or None)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
def suggestions_for_issue(self, issue: Issue) -> set[Suggestion]:
|
def suggestions_for_issue(self, issue: Issue) -> set[Suggestion]:
|
||||||
"""Get suggestions that fix an issue."""
|
"""Get suggestions that fix an issue."""
|
||||||
|
@ -240,7 +240,7 @@ async def test_host_connectivity_disabled(coresys: CoreSys):
|
|||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
assert coresys.host.network.connectivity is True
|
assert coresys.host.network.connectivity is True
|
||||||
await asyncio.sleep(0)
|
await asyncio.sleep(0)
|
||||||
client.async_send_command.assert_called_once_with(
|
client.async_send_command.assert_called_with(
|
||||||
{
|
{
|
||||||
"type": WSType.SUPERVISOR_EVENT,
|
"type": WSType.SUPERVISOR_EVENT,
|
||||||
"data": {
|
"data": {
|
||||||
|
@ -261,3 +261,106 @@ async def test_resolution_apply_suggestion_multiple_copies(coresys: CoreSys):
|
|||||||
assert remove_store_1 in coresys.resolution.suggestions
|
assert remove_store_1 in coresys.resolution.suggestions
|
||||||
assert remove_store_2 not in coresys.resolution.suggestions
|
assert remove_store_2 not in coresys.resolution.suggestions
|
||||||
assert remove_store_3 in coresys.resolution.suggestions
|
assert remove_store_3 in coresys.resolution.suggestions
|
||||||
|
|
||||||
|
|
||||||
|
async def test_events_on_unsupported_changed(coresys: CoreSys):
|
||||||
|
"""Test events fired when unsupported changes."""
|
||||||
|
with patch.object(
|
||||||
|
type(coresys.homeassistant.websocket), "async_send_message"
|
||||||
|
) as send_message:
|
||||||
|
# Marking system as unsupported tells HA
|
||||||
|
assert coresys.resolution.unsupported == []
|
||||||
|
coresys.resolution.unsupported = UnsupportedReason.CONNECTIVITY_CHECK
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
assert coresys.resolution.unsupported == [UnsupportedReason.CONNECTIVITY_CHECK]
|
||||||
|
send_message.assert_called_once_with(
|
||||||
|
_supervisor_event_message(
|
||||||
|
"supported_changed",
|
||||||
|
{"supported": False, "unsupported_reasons": ["connectivity_check"]},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Adding the same reason again does nothing
|
||||||
|
send_message.reset_mock()
|
||||||
|
coresys.resolution.unsupported = UnsupportedReason.CONNECTIVITY_CHECK
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
assert coresys.resolution.unsupported == [UnsupportedReason.CONNECTIVITY_CHECK]
|
||||||
|
send_message.assert_not_called()
|
||||||
|
|
||||||
|
# Adding and removing additional reasons tells HA unsupported reasons changed
|
||||||
|
coresys.resolution.unsupported = UnsupportedReason.JOB_CONDITIONS
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
assert coresys.resolution.unsupported == [
|
||||||
|
UnsupportedReason.CONNECTIVITY_CHECK,
|
||||||
|
UnsupportedReason.JOB_CONDITIONS,
|
||||||
|
]
|
||||||
|
send_message.assert_called_once_with(
|
||||||
|
_supervisor_event_message(
|
||||||
|
"supported_changed",
|
||||||
|
{
|
||||||
|
"supported": False,
|
||||||
|
"unsupported_reasons": ["connectivity_check", "job_conditions"],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
send_message.reset_mock()
|
||||||
|
coresys.resolution.dismiss_unsupported(UnsupportedReason.CONNECTIVITY_CHECK)
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
assert coresys.resolution.unsupported == [UnsupportedReason.JOB_CONDITIONS]
|
||||||
|
send_message.assert_called_once_with(
|
||||||
|
_supervisor_event_message(
|
||||||
|
"supported_changed",
|
||||||
|
{"supported": False, "unsupported_reasons": ["job_conditions"]},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Dismissing all unsupported reasons tells HA its supported again
|
||||||
|
send_message.reset_mock()
|
||||||
|
coresys.resolution.dismiss_unsupported(UnsupportedReason.JOB_CONDITIONS)
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
assert coresys.resolution.unsupported == []
|
||||||
|
send_message.assert_called_once_with(
|
||||||
|
_supervisor_event_message(
|
||||||
|
"supported_changed", {"supported": True, "unsupported_reasons": None}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_events_on_unhealthy_changed(coresys: CoreSys):
|
||||||
|
"""Test events fired when unhealthy changes."""
|
||||||
|
with patch.object(
|
||||||
|
type(coresys.homeassistant.websocket), "async_send_message"
|
||||||
|
) as send_message:
|
||||||
|
# Marking system as unhealthy tells HA
|
||||||
|
assert coresys.resolution.unhealthy == []
|
||||||
|
coresys.resolution.unhealthy = UnhealthyReason.DOCKER
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
assert coresys.resolution.unhealthy == [UnhealthyReason.DOCKER]
|
||||||
|
send_message.assert_called_once_with(
|
||||||
|
_supervisor_event_message(
|
||||||
|
"health_changed",
|
||||||
|
{"healthy": False, "unhealthy_reasons": ["docker"]},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Adding the same reason again does nothing
|
||||||
|
send_message.reset_mock()
|
||||||
|
coresys.resolution.unhealthy = UnhealthyReason.DOCKER
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
assert coresys.resolution.unhealthy == [UnhealthyReason.DOCKER]
|
||||||
|
send_message.assert_not_called()
|
||||||
|
|
||||||
|
# Adding an additional reason tells HA unhealthy reasons changed
|
||||||
|
coresys.resolution.unhealthy = UnhealthyReason.UNTRUSTED
|
||||||
|
await asyncio.sleep(0)
|
||||||
|
assert coresys.resolution.unhealthy == [
|
||||||
|
UnhealthyReason.DOCKER,
|
||||||
|
UnhealthyReason.UNTRUSTED,
|
||||||
|
]
|
||||||
|
send_message.assert_called_once_with(
|
||||||
|
_supervisor_event_message(
|
||||||
|
"health_changed",
|
||||||
|
{"healthy": False, "unhealthy_reasons": ["docker", "untrusted"]},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user