mirror of
https://github.com/home-assistant/core.git
synced 2025-11-27 19:48:01 +00:00
Refresh HassOS coordinator when mount repair is received (#155969)
This commit is contained in:
@@ -128,6 +128,8 @@ ISSUE_KEY_ADDON_PWNED = "issue_addon_pwned"
|
||||
ISSUE_KEY_SYSTEM_FREE_SPACE = "issue_system_free_space"
|
||||
ISSUE_KEY_ADDON_DEPRECATED = "issue_addon_deprecated_addon"
|
||||
|
||||
ISSUE_MOUNT_MOUNT_FAILED = "issue_mount_mount_failed"
|
||||
|
||||
CORE_CONTAINER = "homeassistant"
|
||||
SUPERVISOR_CONTAINER = "hassio_supervisor"
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ from homeassistant.helpers.issue_registry import (
|
||||
)
|
||||
|
||||
from .const import (
|
||||
ADDONS_COORDINATOR,
|
||||
ATTR_DATA,
|
||||
ATTR_HEALTHY,
|
||||
ATTR_STARTUP,
|
||||
@@ -49,6 +50,7 @@ from .const import (
|
||||
ISSUE_KEY_ADDON_PWNED,
|
||||
ISSUE_KEY_SYSTEM_DOCKER_CONFIG,
|
||||
ISSUE_KEY_SYSTEM_FREE_SPACE,
|
||||
ISSUE_MOUNT_MOUNT_FAILED,
|
||||
PLACEHOLDER_KEY_ADDON,
|
||||
PLACEHOLDER_KEY_ADDON_URL,
|
||||
PLACEHOLDER_KEY_FREE_SPACE,
|
||||
@@ -57,7 +59,7 @@ from .const import (
|
||||
STARTUP_COMPLETE,
|
||||
UPDATE_KEY_SUPERVISOR,
|
||||
)
|
||||
from .coordinator import get_addons_info, get_host_info
|
||||
from .coordinator import HassioDataUpdateCoordinator, get_addons_info, get_host_info
|
||||
from .handler import HassIO, get_supervisor_client
|
||||
|
||||
ISSUE_KEY_UNHEALTHY = "unhealthy"
|
||||
@@ -77,7 +79,7 @@ UNSUPPORTED_SKIP_REPAIR = {"privileged"}
|
||||
# Keys (type + context) of issues that when found should be made into a repair
|
||||
ISSUE_KEYS_FOR_REPAIRS = {
|
||||
ISSUE_KEY_ADDON_BOOT_FAIL,
|
||||
"issue_mount_mount_failed",
|
||||
ISSUE_MOUNT_MOUNT_FAILED,
|
||||
"issue_system_multiple_data_disks",
|
||||
"issue_system_reboot_required",
|
||||
ISSUE_KEY_SYSTEM_DOCKER_CONFIG,
|
||||
@@ -284,6 +286,9 @@ class SupervisorIssues:
|
||||
else:
|
||||
placeholders[PLACEHOLDER_KEY_FREE_SPACE] = "<2"
|
||||
|
||||
if issue.key == ISSUE_MOUNT_MOUNT_FAILED:
|
||||
self._async_coordinator_refresh()
|
||||
|
||||
async_create_issue(
|
||||
self._hass,
|
||||
DOMAIN,
|
||||
@@ -336,6 +341,9 @@ class SupervisorIssues:
|
||||
if issue.key in ISSUE_KEYS_FOR_REPAIRS:
|
||||
async_delete_issue(self._hass, DOMAIN, issue.uuid.hex)
|
||||
|
||||
if issue.key == ISSUE_MOUNT_MOUNT_FAILED:
|
||||
self._async_coordinator_refresh()
|
||||
|
||||
del self._issues[issue.uuid]
|
||||
|
||||
def get_issue(self, issue_id: str) -> Issue | None:
|
||||
@@ -406,3 +414,11 @@ class SupervisorIssues:
|
||||
|
||||
elif event[ATTR_WS_EVENT] == EVENT_ISSUE_REMOVED:
|
||||
self.remove_issue(Issue.from_dict(event[ATTR_DATA]))
|
||||
|
||||
def _async_coordinator_refresh(self) -> None:
|
||||
"""Refresh coordinator to update latest data in entities."""
|
||||
coordinator: HassioDataUpdateCoordinator | None
|
||||
if coordinator := self._hass.data.get(ADDONS_COORDINATOR):
|
||||
coordinator.config_entry.async_create_task(
|
||||
self._hass, coordinator.async_refresh()
|
||||
)
|
||||
|
||||
@@ -3,9 +3,18 @@
|
||||
from dataclasses import replace
|
||||
from datetime import timedelta
|
||||
import os
|
||||
from pathlib import PurePath
|
||||
from unittest.mock import AsyncMock, patch
|
||||
from uuid import uuid4
|
||||
|
||||
from aiohasupervisor.models.mounts import CIFSMountResponse, MountsInfo, MountState
|
||||
from aiohasupervisor.models.mounts import (
|
||||
CIFSMountResponse,
|
||||
MountsInfo,
|
||||
MountState,
|
||||
MountType,
|
||||
MountUsage,
|
||||
NFSMountResponse,
|
||||
)
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.hassio import DOMAIN
|
||||
@@ -18,6 +27,7 @@ from .common import MOCK_REPOSITORIES, MOCK_STORE_ADDONS
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
from tests.test_util.aiohttp import AiohttpClientMocker
|
||||
from tests.typing import WebSocketGenerator
|
||||
|
||||
MOCK_ENVIRON = {"SUPERVISOR": "127.0.0.1", "SUPERVISOR_TOKEN": "abcdefgh"}
|
||||
|
||||
@@ -230,16 +240,16 @@ async def test_mount_binary_sensor(
|
||||
assert hass.states.get(entity_id) is None
|
||||
|
||||
# Add a mount.
|
||||
mock_mounts = [
|
||||
mock_mounts: list[CIFSMountResponse | NFSMountResponse] = [
|
||||
CIFSMountResponse(
|
||||
share="files",
|
||||
server="1.2.3.4",
|
||||
name="NAS",
|
||||
type="cifs",
|
||||
usage="share",
|
||||
type=MountType.CIFS,
|
||||
usage=MountUsage.SHARE,
|
||||
read_only=False,
|
||||
state=MountState.ACTIVE,
|
||||
user_path="/share/nas",
|
||||
user_path=PurePath("/share/nas"),
|
||||
)
|
||||
]
|
||||
supervisor_client.mounts.info = AsyncMock(
|
||||
@@ -282,3 +292,115 @@ async def test_mount_binary_sensor(
|
||||
async_fire_time_changed(hass, dt_util.utcnow() + timedelta(seconds=1000))
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
assert hass.states.get(entity_id) is not None
|
||||
|
||||
|
||||
async def test_mount_refresh_after_issue(
|
||||
hass: HomeAssistant,
|
||||
entity_registry: er.EntityRegistry,
|
||||
supervisor_client: AsyncMock,
|
||||
hass_ws_client: WebSocketGenerator,
|
||||
) -> None:
|
||||
"""Test hassio mount state is refreshed after an issue was send by the supervisor."""
|
||||
# Add a mount.
|
||||
mock_mounts: list[CIFSMountResponse | NFSMountResponse] = [
|
||||
CIFSMountResponse(
|
||||
share="files",
|
||||
server="1.2.3.4",
|
||||
name="NAS",
|
||||
type=MountType.CIFS,
|
||||
usage=MountUsage.SHARE,
|
||||
read_only=False,
|
||||
state=MountState.ACTIVE,
|
||||
user_path=PurePath("/share/nas"),
|
||||
)
|
||||
]
|
||||
supervisor_client.mounts.info = AsyncMock(
|
||||
return_value=MountsInfo(default_backup_mount=None, mounts=mock_mounts)
|
||||
)
|
||||
|
||||
config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
with patch.dict(os.environ, MOCK_ENVIRON):
|
||||
result = await async_setup_component(
|
||||
hass,
|
||||
"hassio",
|
||||
{"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}},
|
||||
)
|
||||
assert result
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Enable the entity.
|
||||
entity_id = "binary_sensor.nas_connected"
|
||||
entity_registry.async_update_entity(entity_id, disabled_by=None)
|
||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Test new entity.
|
||||
entity = hass.states.get(entity_id)
|
||||
assert entity is not None
|
||||
assert entity.state == "on"
|
||||
|
||||
# Change mount state to failed, issue a repair, and verify entity's state.
|
||||
mock_mounts[0] = replace(mock_mounts[0], state=MountState.FAILED)
|
||||
client = await hass_ws_client(hass)
|
||||
issue_uuid = uuid4().hex
|
||||
await client.send_json(
|
||||
{
|
||||
"id": 1,
|
||||
"type": "supervisor/event",
|
||||
"data": {
|
||||
"event": "issue_changed",
|
||||
"data": {
|
||||
"uuid": issue_uuid,
|
||||
"type": "mount_failed",
|
||||
"context": "mount",
|
||||
"reference": "nas",
|
||||
"suggestions": [
|
||||
{
|
||||
"uuid": uuid4().hex,
|
||||
"type": "execute_reload",
|
||||
"context": "mount",
|
||||
"reference": "nas",
|
||||
},
|
||||
{
|
||||
"uuid": uuid4().hex,
|
||||
"type": "execute_remove",
|
||||
"context": "mount",
|
||||
"reference": "nas",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
msg = await client.receive_json()
|
||||
assert msg["success"]
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
entity = hass.states.get(entity_id)
|
||||
assert entity is not None
|
||||
assert entity.state == "off"
|
||||
|
||||
# Change mount state to active, issue a repair, and verify entity's state.
|
||||
mock_mounts[0] = replace(mock_mounts[0], state=MountState.ACTIVE)
|
||||
await client.send_json(
|
||||
{
|
||||
"id": 2,
|
||||
"type": "supervisor/event",
|
||||
"data": {
|
||||
"event": "issue_removed",
|
||||
"data": {
|
||||
"uuid": issue_uuid,
|
||||
"type": "mount_failed",
|
||||
"context": "mount",
|
||||
"reference": "nas",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
msg = await client.receive_json()
|
||||
assert msg["success"]
|
||||
await hass.async_block_till_done(wait_background_tasks=True)
|
||||
entity = hass.states.get(entity_id)
|
||||
assert entity is not None
|
||||
assert entity.state == "on"
|
||||
|
||||
Reference in New Issue
Block a user