mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-12-24 20:35:55 +00:00
Use Udisks2 for available data disks (#4202)
* Use Udisks2 for available data disks * pylint issues
This commit is contained in:
@@ -1,13 +1,12 @@
|
||||
"""Test OS API."""
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import PropertyMock, patch
|
||||
|
||||
from aiohttp.test_utils import TestClient
|
||||
import pytest
|
||||
|
||||
from supervisor.coresys import CoreSys
|
||||
from supervisor.hardware.data import Device
|
||||
from supervisor.host.control import SystemControl
|
||||
from supervisor.os.manager import OSManager
|
||||
from supervisor.resolution.const import ContextType, IssueType, SuggestionType
|
||||
from supervisor.resolution.data import Issue, Suggestion
|
||||
@@ -15,6 +14,7 @@ from supervisor.resolution.data import Issue, Suggestion
|
||||
from tests.common import mock_dbus_services
|
||||
from tests.dbus_service_mocks.agent_boards import Boards as BoardsService
|
||||
from tests.dbus_service_mocks.agent_boards_yellow import Yellow as YellowService
|
||||
from tests.dbus_service_mocks.agent_datadisk import DataDisk as DataDiskService
|
||||
from tests.dbus_service_mocks.base import DBusServiceMock
|
||||
|
||||
# pylint: disable=protected-access
|
||||
@@ -49,50 +49,67 @@ async def test_api_os_info_with_agent(api_client: TestClient, coresys: CoreSys):
|
||||
resp = await api_client.get("/os/info")
|
||||
result = await resp.json()
|
||||
|
||||
assert result["data"]["data_disk"] == "/dev/sda"
|
||||
assert result["data"]["data_disk"] == "BJTD4R-0x97cde291"
|
||||
|
||||
|
||||
async def test_api_os_datadisk_move(api_client: TestClient, coresys: CoreSys):
|
||||
"""Test datadisk move without exists disk."""
|
||||
@pytest.mark.parametrize(
|
||||
"new_disk",
|
||||
["/dev/sdaaaa", "/dev/mmcblk1", "Generic-Flash-Disk-61BCDDB6"],
|
||||
ids=["non-existent", "unavailable drive by path", "unavailable drive by id"],
|
||||
)
|
||||
async def test_api_os_datadisk_move_fail(
|
||||
api_client: TestClient, coresys: CoreSys, new_disk: str
|
||||
):
|
||||
"""Test datadisk move to non-existent or invalid devices."""
|
||||
coresys.os._available = True
|
||||
|
||||
resp = await api_client.post("/os/datadisk/move", json={"device": "/dev/sdaaaa"})
|
||||
resp = await api_client.post("/os/datadisk/move", json={"device": new_disk})
|
||||
result = await resp.json()
|
||||
|
||||
assert result["message"] == "'/dev/sdaaaa' don't exists on the host!"
|
||||
assert result["message"] == f"'{new_disk}' not a valid data disk target!"
|
||||
|
||||
|
||||
async def test_api_os_datadisk_list(api_client: TestClient, coresys: CoreSys):
|
||||
"""Test datadisk list function."""
|
||||
coresys.hardware.update_device(
|
||||
Device(
|
||||
"sda",
|
||||
Path("/dev/sda"),
|
||||
Path("/sys/bus/usb/000"),
|
||||
"block",
|
||||
None,
|
||||
[Path("/dev/serial/by-id/test")],
|
||||
{"ID_NAME": "xy", "MINOR": "0", "DEVTYPE": "disk"},
|
||||
[],
|
||||
)
|
||||
)
|
||||
coresys.hardware.update_device(
|
||||
Device(
|
||||
"sda1",
|
||||
Path("/dev/sda1"),
|
||||
Path("/sys/bus/usb/000/1"),
|
||||
"block",
|
||||
None,
|
||||
[Path("/dev/serial/by-id/test1")],
|
||||
{"ID_NAME": "xy", "MINOR": "1", "DEVTYPE": "partition"},
|
||||
[],
|
||||
)
|
||||
)
|
||||
|
||||
resp = await api_client.get("/os/datadisk/list")
|
||||
result = await resp.json()
|
||||
|
||||
assert result["data"]["devices"] == ["/dev/sda"]
|
||||
assert result["data"]["devices"] == ["SSK-SSK-Storage-DF56419883D56"]
|
||||
assert result["data"]["disks"] == [
|
||||
{
|
||||
"vendor": "SSK",
|
||||
"model": "SSK Storage",
|
||||
"serial": "DF56419883D56",
|
||||
"id": "SSK-SSK-Storage-DF56419883D56",
|
||||
"size": 250059350016,
|
||||
"dev_path": "/dev/sda",
|
||||
"name": "SSK SSK Storage (DF56419883D56)",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"new_disk",
|
||||
["SSK-SSK-Storage-DF56419883D56", "/dev/sda"],
|
||||
ids=["by drive id", "by device path"],
|
||||
)
|
||||
async def test_api_os_datadisk_migrate(
|
||||
api_client: TestClient,
|
||||
coresys: CoreSys,
|
||||
os_agent_services: dict[str, DBusServiceMock],
|
||||
new_disk: str,
|
||||
):
|
||||
"""Test migrating datadisk."""
|
||||
datadisk_service: DataDiskService = os_agent_services["agent_datadisk"]
|
||||
datadisk_service.ChangeDevice.calls.clear()
|
||||
coresys.os._available = True
|
||||
|
||||
with patch.object(SystemControl, "reboot") as reboot:
|
||||
resp = await api_client.post("/os/datadisk/move", json={"device": new_disk})
|
||||
assert resp.status == 200
|
||||
|
||||
assert datadisk_service.ChangeDevice.calls == [("/dev/sda",)]
|
||||
reboot.assert_called_once()
|
||||
|
||||
|
||||
async def test_api_board_yellow_info(api_client: TestClient, coresys: CoreSys):
|
||||
|
||||
@@ -30,16 +30,16 @@ async def test_dbus_osagent_datadisk(
|
||||
|
||||
await os_agent.connect(dbus_session_bus)
|
||||
|
||||
assert os_agent.datadisk.current_device.as_posix() == "/dev/sda"
|
||||
assert os_agent.datadisk.current_device.as_posix() == "/dev/mmcblk1"
|
||||
|
||||
datadisk_service.emit_properties_changed({"CurrentDevice": "/dev/sda1"})
|
||||
datadisk_service.emit_properties_changed({"CurrentDevice": "/dev/mmcblk1p1"})
|
||||
await datadisk_service.ping()
|
||||
assert os_agent.datadisk.current_device.as_posix() == "/dev/sda1"
|
||||
assert os_agent.datadisk.current_device.as_posix() == "/dev/mmcblk1p1"
|
||||
|
||||
datadisk_service.emit_properties_changed({}, ["CurrentDevice"])
|
||||
await datadisk_service.ping()
|
||||
await datadisk_service.ping()
|
||||
assert os_agent.datadisk.current_device.as_posix() == "/dev/sda"
|
||||
assert os_agent.datadisk.current_device.as_posix() == "/dev/mmcblk1"
|
||||
|
||||
|
||||
async def test_dbus_osagent_datadisk_change_device(
|
||||
|
||||
@@ -27,7 +27,7 @@ class DataDisk(DBusServiceMock):
|
||||
@dbus_property(access=PropertyAccess.READ)
|
||||
def CurrentDevice(self) -> "s":
|
||||
"""Get Current Device."""
|
||||
return "/dev/sda"
|
||||
return "/dev/mmcblk1"
|
||||
|
||||
@dbus_method()
|
||||
def ChangeDevice(self, arg_0: "s") -> "b":
|
||||
|
||||
@@ -359,6 +359,60 @@ FIXTURES: dict[str, BlockFixture] = {
|
||||
HintSymbolicIconName="",
|
||||
UserspaceMountOptions=[],
|
||||
),
|
||||
"/org/freedesktop/UDisks2/block_devices/multi_part_table1": BlockFixture(
|
||||
Device=b"/dev/parttable1",
|
||||
PreferredDevice=b"/dev/parttable1",
|
||||
Symlinks=[],
|
||||
DeviceNumber=64769,
|
||||
Id="",
|
||||
Size=33554432,
|
||||
ReadOnly=False,
|
||||
Drive="/org/freedesktop/UDisks2/drives/Test_Multiple_Partition_Tables_123456789",
|
||||
MDRaid="/",
|
||||
MDRaidMember="/",
|
||||
IdUsage="",
|
||||
IdType="",
|
||||
IdVersion="",
|
||||
IdLabel="",
|
||||
IdUUID="",
|
||||
Configuration=[],
|
||||
CryptoBackingDevice="/",
|
||||
HintPartitionable=True,
|
||||
HintSystem=True,
|
||||
HintIgnore=False,
|
||||
HintAuto=False,
|
||||
HintName="",
|
||||
HintIconName="",
|
||||
HintSymbolicIconName="",
|
||||
UserspaceMountOptions=[],
|
||||
),
|
||||
"/org/freedesktop/UDisks2/block_devices/multi_part_table2": BlockFixture(
|
||||
Device=b"/dev/parttable2",
|
||||
PreferredDevice=b"/dev/parttable2",
|
||||
Symlinks=[],
|
||||
DeviceNumber=64769,
|
||||
Id="",
|
||||
Size=33554432,
|
||||
ReadOnly=False,
|
||||
Drive="/org/freedesktop/UDisks2/drives/Test_Multiple_Partition_Tables_123456789",
|
||||
MDRaid="/",
|
||||
MDRaidMember="/",
|
||||
IdUsage="",
|
||||
IdType="",
|
||||
IdVersion="",
|
||||
IdLabel="",
|
||||
IdUUID="",
|
||||
Configuration=[],
|
||||
CryptoBackingDevice="/",
|
||||
HintPartitionable=True,
|
||||
HintSystem=True,
|
||||
HintIgnore=False,
|
||||
HintAuto=False,
|
||||
HintName="",
|
||||
HintIconName="",
|
||||
HintSymbolicIconName="",
|
||||
UserspaceMountOptions=[],
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -149,6 +149,37 @@ FIXTURES: dict[str, DriveFixture] = {
|
||||
CanPowerOff=True,
|
||||
SiblingId="/sys/devices/platform/soc/ffe09000.usb/ff500000.usb/xhci-hcd.1.auto/usb2/2-1/2-1.4/2-1.4:1.0",
|
||||
),
|
||||
"/org/freedesktop/UDisks2/drives/Test_Multiple_Partition_Tables_123456789": DriveFixture(
|
||||
Vendor="Test",
|
||||
Model="Multiple Partition Tables",
|
||||
Revision="",
|
||||
Serial="123456789",
|
||||
WWN="",
|
||||
Id="Test-Multiple-Partition-Tables-123456789",
|
||||
Configuration={},
|
||||
Media="",
|
||||
MediaCompatibility=[],
|
||||
MediaRemovable=False,
|
||||
MediaAvailable=True,
|
||||
MediaChangeDetected=True,
|
||||
Size=0,
|
||||
TimeDetected=0,
|
||||
TimeMediaDetected=0,
|
||||
Optical=False,
|
||||
OpticalBlank=False,
|
||||
OpticalNumTracks=0,
|
||||
OpticalNumAudioTracks=0,
|
||||
OpticalNumDataTracks=0,
|
||||
OpticalNumSessions=0,
|
||||
RotationRate=0,
|
||||
ConnectionBus="usb",
|
||||
Seat="seat0",
|
||||
Removable=True,
|
||||
Ejectable=False,
|
||||
SortKey="",
|
||||
CanPowerOff=True,
|
||||
SiblingId="",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,18 @@ class UDisks2Manager(DBusServiceMock):
|
||||
|
||||
interface = "org.freedesktop.UDisks2.Manager"
|
||||
object_path = "/org/freedesktop/UDisks2/Manager"
|
||||
block_devices = [
|
||||
"/org/freedesktop/UDisks2/block_devices/loop0",
|
||||
"/org/freedesktop/UDisks2/block_devices/mmcblk1",
|
||||
"/org/freedesktop/UDisks2/block_devices/mmcblk1p1",
|
||||
"/org/freedesktop/UDisks2/block_devices/mmcblk1p2",
|
||||
"/org/freedesktop/UDisks2/block_devices/mmcblk1p3",
|
||||
"/org/freedesktop/UDisks2/block_devices/sda",
|
||||
"/org/freedesktop/UDisks2/block_devices/sda1",
|
||||
"/org/freedesktop/UDisks2/block_devices/sdb",
|
||||
"/org/freedesktop/UDisks2/block_devices/sdb1",
|
||||
"/org/freedesktop/UDisks2/block_devices/zram1",
|
||||
]
|
||||
|
||||
@dbus_property(access=PropertyAccess.READ)
|
||||
def Version(self) -> "s":
|
||||
@@ -83,18 +95,7 @@ class UDisks2Manager(DBusServiceMock):
|
||||
@dbus_method()
|
||||
def GetBlockDevices(self, options: "a{sv}") -> "ao":
|
||||
"""Do GetBlockDevices method."""
|
||||
return [
|
||||
"/org/freedesktop/UDisks2/block_devices/loop0",
|
||||
"/org/freedesktop/UDisks2/block_devices/mmcblk1",
|
||||
"/org/freedesktop/UDisks2/block_devices/mmcblk1p1",
|
||||
"/org/freedesktop/UDisks2/block_devices/mmcblk1p2",
|
||||
"/org/freedesktop/UDisks2/block_devices/mmcblk1p3",
|
||||
"/org/freedesktop/UDisks2/block_devices/sda",
|
||||
"/org/freedesktop/UDisks2/block_devices/sda1",
|
||||
"/org/freedesktop/UDisks2/block_devices/sdb",
|
||||
"/org/freedesktop/UDisks2/block_devices/sdb1",
|
||||
"/org/freedesktop/UDisks2/block_devices/zram1",
|
||||
]
|
||||
return self.block_devices
|
||||
|
||||
@dbus_method()
|
||||
def ResolveDevice(self, devspec: "a{sv}", options: "a{sv}") -> "ao":
|
||||
|
||||
@@ -41,6 +41,12 @@ FIXTURES: dict[str, PartitionTableFixture] = {
|
||||
"/org/freedesktop/UDisks2/block_devices/sdb": PartitionTableFixture(
|
||||
Partitions=["/org/freedesktop/UDisks2/block_devices/sdb1"], Type="gpt"
|
||||
),
|
||||
"/org/freedesktop/UDisks2/block_devices/multi_part_table1": PartitionTableFixture(
|
||||
Partitions=[], Type="gpt"
|
||||
),
|
||||
"/org/freedesktop/UDisks2/block_devices/multi_part_table2": PartitionTableFixture(
|
||||
Partitions=[], Type="gpt"
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,64 +1,116 @@
|
||||
"""Test OS API."""
|
||||
from pathlib import Path, PosixPath
|
||||
from pathlib import PosixPath
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from supervisor.core import Core
|
||||
from supervisor.coresys import CoreSys
|
||||
from supervisor.exceptions import HassOSDataDiskError
|
||||
from supervisor.hardware.data import Device
|
||||
from supervisor.os.data_disk import Disk
|
||||
|
||||
from tests.common import mock_dbus_services
|
||||
from tests.dbus_service_mocks.agent_datadisk import DataDisk as DataDiskService
|
||||
from tests.dbus_service_mocks.base import DBusServiceMock
|
||||
from tests.dbus_service_mocks.logind import Logind as LogindService
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.fixture(autouse=True)
|
||||
async def add_unusable_drive(
|
||||
coresys: CoreSys,
|
||||
udisks2_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]],
|
||||
):
|
||||
"""Add mock drive with multiple partition tables for negative tests."""
|
||||
await mock_dbus_services(
|
||||
{
|
||||
"udisks2_block": [
|
||||
"/org/freedesktop/UDisks2/block_devices/multi_part_table1",
|
||||
"/org/freedesktop/UDisks2/block_devices/multi_part_table2",
|
||||
],
|
||||
"udisks2_drive": "/org/freedesktop/UDisks2/drives/Test_Multiple_Partition_Tables_123456789",
|
||||
},
|
||||
coresys.dbus.bus,
|
||||
)
|
||||
|
||||
udisks2_services["udisks2_manager"].block_devices = udisks2_services[
|
||||
"udisks2_manager"
|
||||
].block_devices + [
|
||||
"/org/freedesktop/UDisks2/block_devices/multi_part_table1",
|
||||
"/org/freedesktop/UDisks2/block_devices/multi_part_table2",
|
||||
]
|
||||
await coresys.dbus.udisks2.update()
|
||||
|
||||
|
||||
async def tests_datadisk_current(coresys: CoreSys):
|
||||
"""Test current datadisk."""
|
||||
await coresys.dbus.agent.connect(coresys.dbus.bus)
|
||||
await coresys.dbus.agent.update()
|
||||
|
||||
assert coresys.os.datadisk.disk_used == PosixPath("/dev/sda")
|
||||
assert coresys.os.datadisk.disk_used == Disk(
|
||||
vendor="",
|
||||
model="BJTD4R",
|
||||
serial="0x97cde291",
|
||||
id="BJTD4R-0x97cde291",
|
||||
size=31268536320,
|
||||
device_path=PosixPath("/dev/mmcblk1"),
|
||||
object_path="/org/freedesktop/UDisks2/drives/BJTD4R_0x97cde291",
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_datadisk_move(coresys: CoreSys):
|
||||
"""Test datadisk moved without exists device."""
|
||||
await coresys.dbus.agent.connect(coresys.dbus.bus)
|
||||
await coresys.dbus.agent.update()
|
||||
@pytest.mark.parametrize(
|
||||
"new_disk",
|
||||
["/dev/sdaaaa", "/dev/mmcblk1", "Generic-Flash-Disk-61BCDDB6"],
|
||||
ids=["non-existent", "unavailable drive by path", "unavailable drive by id"],
|
||||
)
|
||||
async def test_datadisk_move_fail(coresys: CoreSys, new_disk: str):
|
||||
"""Test datadisk move to non-existent or invalid devices."""
|
||||
coresys.os._available = True
|
||||
|
||||
with pytest.raises(HassOSDataDiskError):
|
||||
await coresys.os.datadisk.migrate_disk(Path("/dev/sdaaaa"))
|
||||
await coresys.os.datadisk.migrate_disk(new_disk)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_datadisk_list(coresys: CoreSys):
|
||||
"""Test docker info api."""
|
||||
await coresys.dbus.agent.connect(coresys.dbus.bus)
|
||||
await coresys.dbus.agent.update()
|
||||
assert {drive.object_path for drive in coresys.dbus.udisks2.drives} == {
|
||||
"/org/freedesktop/UDisks2/drives/BJTD4R_0x97cde291",
|
||||
"/org/freedesktop/UDisks2/drives/Generic_Flash_Disk_61BCDDB6",
|
||||
"/org/freedesktop/UDisks2/drives/SSK_SSK_Storage_DF56419883D56",
|
||||
"/org/freedesktop/UDisks2/drives/Test_Multiple_Partition_Tables_123456789",
|
||||
}
|
||||
|
||||
coresys.hardware.update_device(
|
||||
Device(
|
||||
"sda",
|
||||
Path("/dev/sda"),
|
||||
Path("/sys/bus/usb/000"),
|
||||
"block",
|
||||
None,
|
||||
[Path("/dev/serial/by-id/test")],
|
||||
{"ID_NAME": "xy", "MINOR": "0", "DEVTYPE": "disk"},
|
||||
[],
|
||||
assert coresys.os.datadisk.available_disks == [
|
||||
Disk(
|
||||
vendor="SSK",
|
||||
model="SSK Storage",
|
||||
serial="DF56419883D56",
|
||||
id="SSK-SSK-Storage-DF56419883D56",
|
||||
size=250059350016,
|
||||
device_path=PosixPath("/dev/sda"),
|
||||
object_path="/org/freedesktop/UDisks2/drives/SSK_SSK_Storage_DF56419883D56",
|
||||
)
|
||||
)
|
||||
coresys.hardware.update_device(
|
||||
Device(
|
||||
"sda1",
|
||||
Path("/dev/sda1"),
|
||||
Path("/sys/bus/usb/000/1"),
|
||||
"block",
|
||||
None,
|
||||
[Path("/dev/serial/by-id/test1")],
|
||||
{"ID_NAME": "xy", "MINOR": "1", "DEVTYPE": "partition"},
|
||||
[],
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
assert coresys.os.datadisk.available_disks == [PosixPath("/dev/sda")]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"new_disk",
|
||||
["SSK-SSK-Storage-DF56419883D56", "/dev/sda"],
|
||||
ids=["by drive id", "by device path"],
|
||||
)
|
||||
async def test_datadisk_migrate(
|
||||
coresys: CoreSys,
|
||||
all_dbus_services: dict[str, DBusServiceMock | dict[str, DBusServiceMock]],
|
||||
new_disk: str,
|
||||
):
|
||||
"""Test migrating data disk."""
|
||||
datadisk_service: DataDiskService = all_dbus_services["agent_datadisk"]
|
||||
datadisk_service.ChangeDevice.calls.clear()
|
||||
logind_service: LogindService = all_dbus_services["logind"]
|
||||
logind_service.Reboot.calls.clear()
|
||||
coresys.os._available = True
|
||||
|
||||
with patch.object(Core, "shutdown") as shutdown:
|
||||
await coresys.os.datadisk.migrate_disk(new_disk)
|
||||
shutdown.assert_called_once()
|
||||
|
||||
assert datadisk_service.ChangeDevice.calls == [("/dev/sda",)]
|
||||
assert logind_service.Reboot.calls == [(False,)]
|
||||
|
||||
Reference in New Issue
Block a user