|
1 | 1 | """Test docker addon setup.""" |
2 | 2 |
|
| 3 | +import asyncio |
3 | 4 | from ipaddress import IPv4Address |
| 5 | +from pathlib import Path |
4 | 6 | from typing import Any |
5 | 7 | from unittest.mock import MagicMock, Mock, PropertyMock, patch |
6 | 8 |
|
|
12 | 14 | from supervisor.addons.addon import Addon |
13 | 15 | from supervisor.addons.model import Data |
14 | 16 | from supervisor.addons.options import AddonOptions |
| 17 | +from supervisor.const import BusEvent |
15 | 18 | from supervisor.coresys import CoreSys |
| 19 | +from supervisor.dbus.agent.cgroup import CGroup |
16 | 20 | from supervisor.docker.addon import DockerAddon |
| 21 | +from supervisor.docker.manager import DockerAPI |
17 | 22 | from supervisor.exceptions import CoreDNSError, DockerNotFound |
| 23 | +from supervisor.hardware.data import Device |
| 24 | +from supervisor.os.manager import OSManager |
18 | 25 | from supervisor.plugins.dns import PluginDns |
19 | | -from supervisor.resolution.const import ContextType, IssueType |
20 | | -from supervisor.resolution.data import Issue |
| 26 | +from supervisor.resolution.const import ContextType, IssueType, SuggestionType |
| 27 | +from supervisor.resolution.data import Issue, Suggestion |
21 | 28 |
|
22 | 29 | from ..common import load_json_fixture |
23 | 30 | from . import DEV_MOUNT |
@@ -380,3 +387,113 @@ async def test_addon_stop_delete_host_error( |
380 | 387 | await docker_addon.stop() |
381 | 388 |
|
382 | 389 | capture_exception.assert_called_once_with(err) |
| 390 | + |
| 391 | + |
| 392 | +TEST_DEV_PATH = "/dev/ttyAMA0" |
| 393 | +TEST_SYSFS_PATH = "/sys/devices/platform/soc/ffe09000.usb/ff500000.usb/xhci-hcd.0.auto/usb1/1-1/1-1.1/1-1.1:1.0/tty/ttyACM0" |
| 394 | +TEST_HW_DEVICE = Device( |
| 395 | + name="ttyACM0", |
| 396 | + path=Path("/dev/ttyAMA0"), |
| 397 | + sysfs=Path( |
| 398 | + "/sys/devices/platform/soc/ffe09000.usb/ff500000.usb/xhci-hcd.0.auto/usb1/1-1/1-1.1/1-1.1:1.0/tty/ttyACM0" |
| 399 | + ), |
| 400 | + subsystem="tty", |
| 401 | + parent=Path( |
| 402 | + "/sys/devices/platform/soc/ffe09000.usb/ff500000.usb/xhci-hcd.0.auto/usb1/1-1/1-1.1/1-1.1:1.0" |
| 403 | + ), |
| 404 | + links=[ |
| 405 | + Path( |
| 406 | + "/dev/serial/by-id/usb-Texas_Instruments_TI_CC2531_USB_CDC___0X0123456789ABCDEF-if00" |
| 407 | + ), |
| 408 | + Path("/dev/serial/by-path/platform-xhci-hcd.0.auto-usb-0:1.1:1.0"), |
| 409 | + Path("/dev/serial/by-path/platform-xhci-hcd.0.auto-usbv2-0:1.1:1.0"), |
| 410 | + ], |
| 411 | + attributes={}, |
| 412 | + children=[], |
| 413 | +) |
| 414 | + |
| 415 | + |
| 416 | +@pytest.mark.usefixtures("path_extern") |
| 417 | +@pytest.mark.parametrize( |
| 418 | + ("dev_path", "cgroup", "is_os"), |
| 419 | + [ |
| 420 | + (TEST_DEV_PATH, "1", True), |
| 421 | + (TEST_SYSFS_PATH, "1", True), |
| 422 | + (TEST_DEV_PATH, "1", False), |
| 423 | + (TEST_SYSFS_PATH, "1", False), |
| 424 | + (TEST_DEV_PATH, "2", True), |
| 425 | + (TEST_SYSFS_PATH, "2", True), |
| 426 | + ], |
| 427 | +) |
| 428 | +async def test_addon_new_device( |
| 429 | + coresys: CoreSys, |
| 430 | + install_addon_ssh: Addon, |
| 431 | + container: MagicMock, |
| 432 | + docker: DockerAPI, |
| 433 | + dev_path: str, |
| 434 | + cgroup: str, |
| 435 | + is_os: bool, |
| 436 | +): |
| 437 | + """Test new device that is listed in static devices.""" |
| 438 | + coresys.hardware.disk.get_disk_free_space = lambda x: 5000 |
| 439 | + install_addon_ssh.data["devices"] = [dev_path] |
| 440 | + container.id = 123 |
| 441 | + docker.info.cgroup = cgroup |
| 442 | + |
| 443 | + with ( |
| 444 | + patch.object(Addon, "write_options"), |
| 445 | + patch.object(OSManager, "available", new=PropertyMock(return_value=is_os)), |
| 446 | + patch.object(CGroup, "add_devices_allowed") as add_devices, |
| 447 | + ): |
| 448 | + await install_addon_ssh.start() |
| 449 | + |
| 450 | + coresys.bus.fire_event( |
| 451 | + BusEvent.HARDWARE_NEW_DEVICE, |
| 452 | + TEST_HW_DEVICE, |
| 453 | + ) |
| 454 | + await asyncio.sleep(0.01) |
| 455 | + |
| 456 | + add_devices.assert_called_once_with(123, "c 0:0 rwm") |
| 457 | + |
| 458 | + |
| 459 | +@pytest.mark.usefixtures("path_extern") |
| 460 | +@pytest.mark.parametrize("dev_path", [TEST_DEV_PATH, TEST_SYSFS_PATH]) |
| 461 | +async def test_addon_new_device_no_haos( |
| 462 | + coresys: CoreSys, |
| 463 | + install_addon_ssh: Addon, |
| 464 | + docker: DockerAPI, |
| 465 | + dev_path: str, |
| 466 | +): |
| 467 | + """Test new device that is listed in static devices on non HAOS system with CGroup V2.""" |
| 468 | + coresys.hardware.disk.get_disk_free_space = lambda x: 5000 |
| 469 | + install_addon_ssh.data["devices"] = [dev_path] |
| 470 | + docker.info.cgroup = "2" |
| 471 | + |
| 472 | + with ( |
| 473 | + patch.object(Addon, "write_options"), |
| 474 | + patch.object(OSManager, "available", new=PropertyMock(return_value=False)), |
| 475 | + patch.object(CGroup, "add_devices_allowed") as add_devices, |
| 476 | + ): |
| 477 | + await install_addon_ssh.start() |
| 478 | + |
| 479 | + coresys.bus.fire_event( |
| 480 | + BusEvent.HARDWARE_NEW_DEVICE, |
| 481 | + TEST_HW_DEVICE, |
| 482 | + ) |
| 483 | + await asyncio.sleep(0.01) |
| 484 | + |
| 485 | + add_devices.assert_not_called() |
| 486 | + |
| 487 | + # Issue added with hardware event since access cannot be added dynamically |
| 488 | + assert install_addon_ssh.device_access_missing_issue in coresys.resolution.issues |
| 489 | + assert ( |
| 490 | + Suggestion( |
| 491 | + SuggestionType.EXECUTE_RESTART, ContextType.ADDON, reference="local_ssh" |
| 492 | + ) |
| 493 | + in coresys.resolution.suggestions |
| 494 | + ) |
| 495 | + |
| 496 | + # Stopping and removing the container clears it as access granted on next start |
| 497 | + await install_addon_ssh.stop() |
| 498 | + assert coresys.resolution.issues == [] |
| 499 | + assert coresys.resolution.suggestions == [] |
0 commit comments