DataDisk reload devices (#3129)

* DataDisk reload devices

* improve loading

* simplify

* validate input device

* add comments

* Add agent version to API

* more tests

* fix test lint
This commit is contained in:
Pascal Vizeli 2021-09-20 12:52:51 +02:00 committed by GitHub
parent 74530baeb7
commit 53eae96a98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 109 additions and 6 deletions

View File

@ -6,3 +6,4 @@ ATTR_DT_UTC = "dt_utc"
ATTR_DT_SYNCHRONIZED = "dt_synchronized"
ATTR_DISK_DATA = "disk_data"
ATTR_DEVICE = "device"
ATTR_AGENT_VERSION = "agent_version"

View File

@ -25,7 +25,13 @@ from ..const import (
CONTENT_TYPE_BINARY,
)
from ..coresys import CoreSysAttributes
from .const import ATTR_DT_SYNCHRONIZED, ATTR_DT_UTC, ATTR_USE_NTP, ATTR_USE_RTC
from .const import (
ATTR_AGENT_VERSION,
ATTR_DT_SYNCHRONIZED,
ATTR_DT_UTC,
ATTR_USE_NTP,
ATTR_USE_RTC,
)
from .utils import api_process, api_process_raw, api_validate
SERVICE = "service"
@ -40,6 +46,7 @@ class APIHost(CoreSysAttributes):
async def info(self, request):
"""Return host information."""
return {
ATTR_AGENT_VERSION: self.sys_dbus.agent.version,
ATTR_CHASSIS: self.sys_host.info.chassis,
ATTR_CPE: self.sys_host.info.cpe,
ATTR_DEPLOYMENT: self.sys_host.info.deployment,

View File

@ -37,5 +37,10 @@ class DataDisk(DBusInterface):
@dbus_connected
async def change_device(self, device: Path) -> None:
"""Load/Update AppArmor profile."""
"""Migrate data disk to a new device."""
await self.dbus.DataDisk.ChangeDevice(device.as_posix())
@dbus_connected
async def reload_device(self) -> None:
"""Reload device data."""
await self.dbus.DataDisk.ReloadDevice()

View File

@ -107,6 +107,10 @@ class HassOSJobError(HassOSError, JobException):
"""Function not supported by HassOS."""
class HassOSDataDiskError(HassOSError):
"""Issues with the DataDisk feature from HAOS."""
# HaCli
@ -278,6 +282,10 @@ class DBusFatalError(DBusError):
"""DBus call going wrong."""
class DBusInterfaceMethodError(DBusInterfaceError):
"""Dbus method was not definied."""
class DBusParseError(DBusError):
"""DBus parse error."""

View File

@ -3,8 +3,18 @@ import logging
from pathlib import Path
from typing import Optional
from awesomeversion import AwesomeVersion
from ..coresys import CoreSys, CoreSysAttributes
from ..exceptions import DBusError, HassOSError, HassOSJobError, HostError
from ..exceptions import (
DBusError,
HardwareNotFound,
HassOSDataDiskError,
HassOSError,
HassOSJobError,
HostError,
)
from ..hardware.const import UdevSubsystem
from ..jobs.const import JobCondition, JobExecutionLimit
from ..jobs.decorator import Job
@ -23,6 +33,13 @@ class DataDisk(CoreSysAttributes):
"""Return Path to used Disk for data."""
return self.sys_dbus.agent.datadisk.current_device
@Job(conditions=[JobCondition.OS_AGENT])
async def load(self) -> None:
"""Load DataDisk feature."""
# Update datadisk details on OS-Agent
if self.sys_dbus.agent.version >= AwesomeVersion("1.2.0"):
await self.sys_dbus.agent.datadisk.reload_device()
@Job(
conditions=[JobCondition.HAOS, JobCondition.OS_AGENT, JobCondition.HEALTHY],
limit=JobExecutionLimit.ONCE,
@ -30,14 +47,32 @@ class DataDisk(CoreSysAttributes):
)
async def migrate_disk(self, new_disk: Path) -> None:
"""Move data partition to a new disk."""
# Need some error handling, but we need know what disk_used will return
# Validate integrity of the data input
try:
device = self.sys_hardware.get_by_path(new_disk)
except HardwareNotFound:
raise HassOSDataDiskError(
f"'{new_disk!s}' don't exists on the host!", _LOGGER.error
) from None
if device.subsystem != UdevSubsystem.DISK:
raise HassOSDataDiskError(
f"'{new_disk!s}' is not a harddisk!", _LOGGER.error
)
if self.sys_hardware.disk.is_system_partition(device):
raise HassOSDataDiskError(
f"'{new_disk}' is a system disk and can't be used!", _LOGGER.error
)
# Migrate data on Host
try:
await self.sys_dbus.agent.datadisk.change_device(new_disk)
except DBusError as err:
raise HassOSError(
raise HassOSDataDiskError(
f"Can't move data partition to {new_disk!s}: {err!s}", _LOGGER.error
) from err
# Restart Host for finish the process
try:
await self.sys_host.control.reboot()
except HostError as err:

View File

@ -146,6 +146,7 @@ class OSManager(CoreSysAttributes):
self._os_name = cpe.get_product()[0]
await self.sys_dbus.rauc.update()
await self.datadisk.load()
_LOGGER.info(
"Detect Home Assistant Operating System %s / BootSlot %s",

View File

@ -16,6 +16,7 @@ from . import clean_env
from ..exceptions import (
DBusFatalError,
DBusInterfaceError,
DBusInterfaceMethodError,
DBusNotConnectedError,
DBusParseError,
DBusProgramError,
@ -296,7 +297,7 @@ class DBusCallWrapper:
def __call__(self) -> None:
"""Catch this method from being called."""
_LOGGER.error("D-Bus method %s not exists!", self.interface)
raise DBusFatalError()
raise DBusInterfaceMethodError()
def __getattr__(self, name: str):
"""Map to dbus method."""

View File

@ -1,6 +1,10 @@
"""Test OS API."""
import pytest
from supervisor.coresys import CoreSys
# pylint: disable=protected-access
@pytest.mark.asyncio
async def test_api_os_info(api_client):
@ -17,3 +21,28 @@ async def test_api_os_info(api_client):
"disk_data",
):
assert attr in result["data"]
@pytest.mark.asyncio
async def test_api_os_info_with_agent(api_client, coresys: CoreSys):
"""Test docker info api."""
await coresys.dbus.agent.connect()
await coresys.dbus.agent.update()
resp = await api_client.get("/os/info")
result = await resp.json()
assert result["data"]["disk_data"] == "/dev/sda"
@pytest.mark.asyncio
async def test_api_os_move_data(api_client, coresys: CoreSys):
"""Test docker info api."""
await coresys.dbus.agent.connect()
await coresys.dbus.agent.update()
coresys.os._available = True
resp = await api_client.post("/os/datadisk/move", json={"device": "/dev/sdaaaa"})
result = await resp.json()
assert result["message"] == "'/dev/sdaaaa' don't exists on the host!"

View File

@ -26,3 +26,14 @@ async def test_dbus_osagent_datadisk_change_device(coresys: CoreSys):
await coresys.dbus.agent.connect()
assert await coresys.dbus.agent.datadisk.change_device(Path("/dev/sdb")) is None
async def test_dbus_osagent_datadisk_reload_device(coresys: CoreSys):
"""Change datadisk on device."""
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.agent.datadisk.reload_device()
await coresys.dbus.agent.connect()
assert await coresys.dbus.agent.datadisk.reload_device() is None

View File

@ -0,0 +1 @@
(<true>,)

View File

@ -45,6 +45,10 @@
<arg type="b" direction="out">
</arg>
</method>
<method name="ReloadDevice">
<arg type="b" direction="out">
</arg>
</method>
<property name="CurrentDevice" type="s" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true">
</annotation>