OS-Agent support (#2811)

* OS-Agent support

* add agent to host feature

* Add support for os-agent on devcontainer

* Rename core

* fix tests

* add setter

* add cgroup / apparmor

* all interfaces added

* fix import

* Add tests

* More tests

* Finish tests

* reformating xml files

* fix doc string

* address comments

* change return value

* fix tests

* Update supervisor/dbus/agent/__init__.py

Co-authored-by: Joakim Sørensen <joasoe@gmail.com>

* Update scripts/supervisor.sh

Co-authored-by: Joakim Sørensen <joasoe@gmail.com>

Co-authored-by: Joakim Sørensen <joasoe@gmail.com>
This commit is contained in:
Pascal Vizeli 2021-06-30 11:55:53 +02:00 committed by GitHub
parent 7c6c982414
commit 2b6829a786
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 807 additions and 93 deletions

View File

@ -48,6 +48,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
jq \
dbus \
network-manager \
apparmor-utils \
libpulse0 \
&& bash <(curl https://getvcn.codenotary.com -L) \
&& rm -rf /var/lib/apt/lists/*

View File

@ -88,6 +88,21 @@ function init_udev() {
udevadm trigger && udevadm settle
}
function init_os-agent() {
if pgrep os-agent; then
echo "os-agent is running"
return 0
fi
if [ ! -f /usr/sbin/os-agent ]; then
curl -Lo /usr/sbin/os-agent https://github.com/home-assistant/os-agent/releases/latest/download/os-agent-debian-amd64.bin
curl -Lo /etc/dbus-1/system.d/io.hass.conf https://raw.githubusercontent.com/home-assistant/os-agent/main/contrib/io.hass.conf
chmod a+x /usr/sbin/os-agent
fi
/usr/sbin/os-agent &
}
echo "Run Supervisor"
start_docker
@ -99,6 +114,7 @@ if [ "$( docker container inspect -f '{{.State.Status}}' hassio_supervisor )" ==
docker rm -f hassio_supervisor
init_dbus
init_udev
init_os-agent
cleanup_lastboot
run_supervisor
stop_docker
@ -111,6 +127,7 @@ else
cleanup_docker
init_dbus
init_udev
init_os-agent
run_supervisor
stop_docker
fi

View File

@ -33,7 +33,7 @@ from .const import (
)
from .core import Core
from .coresys import CoreSys
from .dbus import DBusManager
from .dbus.manager import DBusManager
from .discovery import Discovery
from .hardware.module import HardwareManager
from .hassos import HassOS

View File

@ -21,7 +21,7 @@ if TYPE_CHECKING:
from .arch import CpuArch
from .auth import Auth
from .core import Core
from .dbus import DBusManager
from .dbus.manager import DBusManager
from .discovery import Discovery
from .hardware.module import HardwareManager
from .hassos import HassOS

View File

@ -1,84 +1 @@
"""D-Bus interface objects."""
import logging
from typing import List
from ..const import SOCKET_DBUS
from ..coresys import CoreSys, CoreSysAttributes
from .hostname import Hostname
from .interface import DBusInterface
from .logind import Logind
from .network import NetworkManager
from .rauc import Rauc
from .systemd import Systemd
from .timedate import TimeDate
_LOGGER: logging.Logger = logging.getLogger(__name__)
class DBusManager(CoreSysAttributes):
"""A DBus Interface handler."""
def __init__(self, coresys: CoreSys) -> None:
"""Initialize D-Bus interface."""
self.coresys: CoreSys = coresys
self._systemd: Systemd = Systemd()
self._logind: Logind = Logind()
self._hostname: Hostname = Hostname()
self._rauc: Rauc = Rauc()
self._network: NetworkManager = NetworkManager()
self._timedate: TimeDate = TimeDate()
@property
def systemd(self) -> Systemd:
"""Return the systemd interface."""
return self._systemd
@property
def logind(self) -> Logind:
"""Return the logind interface."""
return self._logind
@property
def timedate(self) -> TimeDate:
"""Return the timedate interface."""
return self._timedate
@property
def hostname(self) -> Hostname:
"""Return the hostname interface."""
return self._hostname
@property
def rauc(self) -> Rauc:
"""Return the rauc interface."""
return self._rauc
@property
def network(self) -> NetworkManager:
"""Return NetworkManager interface."""
return self._network
async def load(self) -> None:
"""Connect interfaces to D-Bus."""
if not SOCKET_DBUS.exists():
_LOGGER.error(
"No D-Bus support on Host. Disabled any kind of host control!"
)
return
dbus_loads: List[DBusInterface] = [
self.systemd,
self.logind,
self.hostname,
self.timedate,
self.network,
self.rauc,
]
for dbus in dbus_loads:
try:
await dbus.connect()
except Exception as err: # pylint: disable=broad-except
_LOGGER.warning("Can't load dbus interface %s: %s", dbus.name, err)
self.sys_host.supported_features.cache_clear()
"""D-Bus interfaces."""

View File

@ -0,0 +1,99 @@
"""OS-Agent implementation for DBUS."""
import asyncio
import logging
from typing import Any, Dict
from awesomeversion import AwesomeVersion
from ...exceptions import DBusError, DBusInterfaceError
from ...utils.gdbus import DBus
from ..const import (
DBUS_ATTR_DIAGNOSTICS,
DBUS_ATTR_VERSION,
DBUS_NAME_HAOS,
DBUS_OBJECT_HAOS,
)
from ..interface import DBusInterface, dbus_property
from ..utils import dbus_connected
from .apparmor import AppArmor
from .cgroup import CGroup
from .datadisk import DataDisk
from .system import System
_LOGGER: logging.Logger = logging.getLogger(__name__)
class OSAgent(DBusInterface):
"""Handle D-Bus interface for OS-Agent."""
name = DBUS_NAME_HAOS
def __init__(self) -> None:
"""Initialize Properties."""
self.properties: Dict[str, Any] = {}
self._cgroup: CGroup = CGroup()
self._apparmor: AppArmor = AppArmor()
self._system: System = System()
self._datadisk: DataDisk = DataDisk()
@property
def cgroup(self) -> CGroup:
"""Return CGroup DBUS object."""
return self._cgroup
@property
def apparmor(self) -> AppArmor:
"""Return AppArmor DBUS object."""
return self._apparmor
@property
def system(self) -> System:
"""Return System DBUS object."""
return self._system
@property
def datadisk(self) -> DataDisk:
"""Return DataDisk DBUS object."""
return self._datadisk
@property
@dbus_property
def version(self) -> AwesomeVersion:
"""Return version of OS-Agent."""
return AwesomeVersion(self.properties[DBUS_ATTR_VERSION])
@property
@dbus_property
def diagnostics(self) -> bool:
"""Return if diagnostics is enabled on OS-Agent."""
return self.properties[DBUS_ATTR_DIAGNOSTICS]
@diagnostics.setter
def diagnostics(self, value: bool) -> None:
"""Enable or disable OS-Agent diagnostics."""
asyncio.create_task(
self.dbus.set_property(DBUS_NAME_HAOS, DBUS_ATTR_DIAGNOSTICS, value)
)
async def connect(self) -> None:
"""Connect to system's D-Bus."""
try:
self.dbus = await DBus.connect(DBUS_NAME_HAOS, DBUS_OBJECT_HAOS)
await self.cgroup.connect()
await self.apparmor.connect()
await self.system.connect()
await self.datadisk.connect()
except DBusError:
_LOGGER.warning("Can't connect to OS-Agent")
except DBusInterfaceError:
_LOGGER.warning(
"No OS-Agent support on the host. Some Host functions have been disabled."
)
@dbus_connected
async def update(self):
"""Update Properties."""
self.properties = await self.dbus.get_properties(DBUS_NAME_HAOS)
await self.apparmor.update()
await self.datadisk.update()

View File

@ -0,0 +1,48 @@
"""AppArmor object for OS-Agent."""
from pathlib import Path
from typing import Any, Dict
from awesomeversion import AwesomeVersion
from ...utils.gdbus import DBus
from ..const import (
DBUS_ATTR_PARSER_VERSION,
DBUS_NAME_HAOS,
DBUS_NAME_HAOS_APPARMOR,
DBUS_OBJECT_HAOS_APPARMOR,
)
from ..interface import DBusInterface, dbus_property
from ..utils import dbus_connected
class AppArmor(DBusInterface):
"""AppArmor object for OS Agent."""
def __init__(self) -> None:
"""Initialize Properties."""
self.properties: Dict[str, Any] = {}
@property
@dbus_property
def version(self) -> AwesomeVersion:
"""Return version of host AppArmor parser."""
return AwesomeVersion(self.properties[DBUS_ATTR_PARSER_VERSION])
async def connect(self) -> None:
"""Get connection information."""
self.dbus = await DBus.connect(DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_APPARMOR)
@dbus_connected
async def update(self):
"""Update Properties."""
self.properties = await self.dbus.get_properties(DBUS_NAME_HAOS_APPARMOR)
@dbus_connected
async def load_profile(self, profile: Path, cache: Path) -> None:
"""Load/Update AppArmor profile."""
await self.dbus.AppArmor.LoadProfile(profile.as_posix(), cache.as_posix())
@dbus_connected
async def unload_profile(self, profile: Path, cache: Path) -> None:
"""Remove AppArmor profile."""
await self.dbus.AppArmor.UnloadProfile(profile.as_posix(), cache.as_posix())

View File

@ -0,0 +1,19 @@
"""CGroup object for OS-Agent."""
from ...utils.gdbus import DBus
from ..const import DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_CGROUP
from ..interface import DBusInterface
from ..utils import dbus_connected
class CGroup(DBusInterface):
"""CGroup object for OS Agent."""
async def connect(self) -> None:
"""Get connection information."""
self.dbus = await DBus.connect(DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_CGROUP)
@dbus_connected
async def add_devices_allowed(self, container_id: str, permission: str) -> None:
"""Update cgroup devices and add new devices."""
await self.dbus.CGroup.AddDevicesAllowed(container_id, permission)

View File

@ -0,0 +1,41 @@
"""DataDisk object for OS-Agent."""
from pathlib import Path
from typing import Any, Dict
from ...utils.gdbus import DBus
from ..const import (
DBUS_ATTR_CURRENT_DEVICE,
DBUS_NAME_HAOS,
DBUS_NAME_HAOS_DATADISK,
DBUS_OBJECT_HAOS_DATADISK,
)
from ..interface import DBusInterface, dbus_property
from ..utils import dbus_connected
class DataDisk(DBusInterface):
"""DataDisk object for OS Agent."""
def __init__(self) -> None:
"""Initialize Properties."""
self.properties: Dict[str, Any] = {}
@property
@dbus_property
def current_device(self) -> Path:
"""Return current device used for data."""
return Path(self.properties[DBUS_ATTR_CURRENT_DEVICE])
async def connect(self) -> None:
"""Get connection information."""
self.dbus = await DBus.connect(DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_DATADISK)
@dbus_connected
async def update(self):
"""Update Properties."""
self.properties = await self.dbus.get_properties(DBUS_NAME_HAOS_DATADISK)
@dbus_connected
async def change_device(self, device: Path) -> None:
"""Load/Update AppArmor profile."""
await self.dbus.DataDisk.ChangeDevice(device.as_posix())

View File

@ -0,0 +1,19 @@
"""System object for OS-Agent."""
from ...utils.gdbus import DBus
from ..const import DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_SYSTEM
from ..interface import DBusInterface
from ..utils import dbus_connected
class System(DBusInterface):
"""System object for OS Agent."""
async def connect(self) -> None:
"""Get connection information."""
self.dbus = await DBus.connect(DBUS_NAME_HAOS, DBUS_OBJECT_HAOS_SYSTEM)
@dbus_connected
async def schedule_wipe_device(self) -> None:
"""Schedule a factory reset on next system boot."""
await self.dbus.System.ScheduleWipeDevice()

View File

@ -20,6 +20,12 @@ DBUS_NAME_NM_CONNECTION_ACTIVE_CHANGED = (
DBUS_NAME_SYSTEMD = "org.freedesktop.systemd1"
DBUS_NAME_LOGIND = "org.freedesktop.login1"
DBUS_NAME_TIMEDATE = "org.freedesktop.timedate1"
DBUS_NAME_HAOS = "io.hass.os"
DBUS_NAME_HAOS_CGROUP = "io.hass.os.CGroup"
DBUS_NAME_HAOS_APPARMOR = "io.hass.os.AppArmor"
DBUS_NAME_HAOS_SYSTEM = "io.hass.os.System"
DBUS_NAME_HAOS_DATADISK = "io.hass.os.DataDisk"
DBUS_OBJECT_BASE = "/"
DBUS_OBJECT_DNS = "/org/freedesktop/NetworkManager/DnsManager"
@ -29,6 +35,11 @@ DBUS_OBJECT_NM = "/org/freedesktop/NetworkManager"
DBUS_OBJECT_SYSTEMD = "/org/freedesktop/systemd1"
DBUS_OBJECT_LOGIND = "/org/freedesktop/login1"
DBUS_OBJECT_TIMEDATE = "/org/freedesktop/timedate1"
DBUS_OBJECT_HAOS = "/io/hass/os"
DBUS_OBJECT_HAOS_CGROUP = "/io/hass/os/CGroup"
DBUS_OBJECT_HAOS_APPARMOR = "/io/hass/os/AppArmor"
DBUS_OBJECT_HAOS_SYSTEM = "/io/hass/os/System"
DBUS_OBJECT_HAOS_DATADISK = "/io/hass/os/DataDisk"
DBUS_ATTR_ACTIVE_CONNECTIONS = "ActiveConnections"
DBUS_ATTR_ACTIVE_CONNECTION = "ActiveConnection"
@ -77,6 +88,9 @@ DBUS_ATTR_LOCALRTC = "LocalRTC"
DBUS_ATTR_NTP = "NTP"
DBUS_ATTR_NTPSYNCHRONIZED = "NTPSynchronized"
DBUS_ATTR_TIMEUSEC = "TimeUSec"
DBUS_ATTR_DIAGNOSTICS = "Diagnostics"
DBUS_ATTR_CURRENT_DEVICE = "CurrentDevice"
DBUS_ATTR_PARSER_VERSION = "ParserVersion"
class RaucState(str, Enum):

View File

@ -0,0 +1,93 @@
"""D-Bus interface objects."""
import logging
from typing import List
from ..const import SOCKET_DBUS
from ..coresys import CoreSys, CoreSysAttributes
from .agent import OSAgent
from .hostname import Hostname
from .interface import DBusInterface
from .logind import Logind
from .network import NetworkManager
from .rauc import Rauc
from .systemd import Systemd
from .timedate import TimeDate
_LOGGER: logging.Logger = logging.getLogger(__name__)
class DBusManager(CoreSysAttributes):
"""A DBus Interface handler."""
def __init__(self, coresys: CoreSys) -> None:
"""Initialize D-Bus interface."""
self.coresys: CoreSys = coresys
self._systemd: Systemd = Systemd()
self._logind: Logind = Logind()
self._hostname: Hostname = Hostname()
self._rauc: Rauc = Rauc()
self._network: NetworkManager = NetworkManager()
self._agent: OSAgent = OSAgent()
self._timedate: TimeDate = TimeDate()
@property
def systemd(self) -> Systemd:
"""Return the systemd interface."""
return self._systemd
@property
def logind(self) -> Logind:
"""Return the logind interface."""
return self._logind
@property
def hostname(self) -> Hostname:
"""Return the hostname interface."""
return self._hostname
@property
def rauc(self) -> Rauc:
"""Return the rauc interface."""
return self._rauc
@property
def network(self) -> NetworkManager:
"""Return NetworkManager interface."""
return self._network
@property
def agent(self) -> OSAgent:
"""Return OS-Agent interface."""
return self._agent
@property
def timedate(self) -> TimeDate:
"""Return the timedate interface."""
return self._timedate
async def load(self) -> None:
"""Connect interfaces to D-Bus."""
if not SOCKET_DBUS.exists():
_LOGGER.error(
"No D-Bus support on Host. Disabled any kind of host control!"
)
return
dbus_loads: List[DBusInterface] = [
self.agent,
self.systemd,
self.logind,
self.hostname,
self.timedate,
self.network,
self.rauc,
]
for dbus in dbus_loads:
_LOGGER.info("Load dbus interface %s", dbus.name)
try:
await dbus.connect()
except Exception as err: # pylint: disable=broad-except
_LOGGER.warning("Can't load dbus interface %s: %s", dbus.name, err)
self.sys_host.supported_features.cache_clear()

View File

@ -4,10 +4,10 @@ from functools import lru_cache
import logging
from typing import List
from ..const import HostFeature
from ..coresys import CoreSys, CoreSysAttributes
from ..exceptions import HassioError, PulseAudioError
from .apparmor import AppArmorControl
from .const import HostFeature
from .control import SystemControl
from .info import InfoCenter
from .network import NetworkManager
@ -85,8 +85,11 @@ class HostManager(CoreSysAttributes):
if self.sys_dbus.timedate.is_connected:
features.append(HostFeature.TIMEDATE)
if self.sys_dbus.agent.is_connected:
features.append(HostFeature.AGENT)
if self.sys_hassos.available:
features.append(HostFeature.HASSOS)
features.append(HostFeature.HAOS)
return features
@ -100,6 +103,9 @@ class HostManager(CoreSysAttributes):
if self.sys_dbus.network.is_connected:
await self.network.update()
if self.sys_dbus.agent.is_connected:
await self.sys_dbus.agent.update()
with suppress(PulseAudioError):
await self.sound.update()

View File

@ -33,3 +33,16 @@ class WifiMode(str, Enum):
MESH = "mesh"
ADHOC = "adhoc"
AP = "ap"
class HostFeature(str, Enum):
"""Host feature."""
HAOS = "haos"
HOSTNAME = "hostname"
NETWORK = "network"
REBOOT = "reboot"
SERVICES = "services"
SHUTDOWN = "shutdown"
AGENT = "agent"
TIMEDATE = "timedate"

View File

@ -1,7 +1,7 @@
"""A collection of tasks."""
import logging
from ..const import AddonState, HostFeature
from ..const import AddonState
from ..coresys import CoreSysAttributes
from ..exceptions import (
AddonsError,
@ -12,6 +12,7 @@ from ..exceptions import (
MulticastError,
ObserverError,
)
from ..host.const import HostFeature
from ..jobs.decorator import Job, JobCondition
_LOGGER: logging.Logger = logging.getLogger(__name__)

View File

@ -1,8 +1,9 @@
"""Evaluation class for network manager."""
from typing import List
from ...const import CoreState, HostFeature
from ...const import CoreState
from ...coresys import CoreSys
from ...host.const import HostFeature
from ..const import UnsupportedReason
from .base import EvaluateBase

View File

@ -1,8 +1,9 @@
"""Evaluation class for systemd."""
from typing import List
from ...const import CoreState, HostFeature
from ...const import CoreState
from ...coresys import CoreSys
from ...host.const import HostFeature
from ..const import UnsupportedReason
from .base import EvaluateBase

View File

@ -67,6 +67,7 @@ MONITOR: str = "gdbus monitor --system --dest {bus}"
WAIT: str = "gdbus wait --system --activate {bus} --timeout 5 {bus}"
DBUS_METHOD_GETALL: str = "org.freedesktop.DBus.Properties.GetAll"
DBUS_METHOD_SET: str = "org.freedesktop.DBus.Properties.Set"
def _convert_bytes(value: str) -> str:
@ -225,6 +226,16 @@ class DBus:
_LOGGER.error("No attributes returned for %s", interface)
raise DBusFatalError() from err
async def set_property(
self, interface: str, name: str, value: Any
) -> Dict[str, Any]:
"""Set a property from interface."""
try:
return (await self.call_dbus(DBUS_METHOD_SET, interface, name, value))[0]
except IndexError as err:
_LOGGER.error("No Set attribute %s for %s", name, interface)
raise DBusFatalError() from err
async def _send(self, command: List[str], silent=False) -> str:
"""Send command over dbus."""
# Run command

View File

@ -0,0 +1 @@
"""Tests for OS-Agent."""

View File

@ -0,0 +1,15 @@
"""Test OSAgent dbus interface."""
from supervisor.coresys import CoreSys
async def test_dbus_osagent(coresys: CoreSys):
"""Test coresys dbus connection."""
assert coresys.dbus.agent.version is None
assert coresys.dbus.agent.diagnostics is None
await coresys.dbus.agent.connect()
await coresys.dbus.agent.update()
assert coresys.dbus.agent.version == "1.1.0"
assert coresys.dbus.agent.diagnostics

View File

@ -0,0 +1,53 @@
"""Test AppArmor/Agent dbus interface."""
from pathlib import Path
import pytest
from supervisor.coresys import CoreSys
from supervisor.exceptions import DBusNotConnectedError
async def test_dbus_osagent_apparmor(coresys: CoreSys):
"""Test coresys dbus connection."""
assert coresys.dbus.agent.apparmor.version is None
await coresys.dbus.agent.connect()
await coresys.dbus.agent.update()
assert coresys.dbus.agent.apparmor.version == "2.13.2"
async def test_dbus_osagent_apparmor_load(coresys: CoreSys):
"""Load AppArmor Profile on host."""
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.agent.apparmor.load_profile(
Path("/data/apparmor/profile"), Path("/data/apparmor/cache")
)
await coresys.dbus.agent.connect()
assert (
await coresys.dbus.agent.apparmor.load_profile(
Path("/data/apparmor/profile"), Path("/data/apparmor/cache")
)
is None
)
async def test_dbus_osagent_apparmor_unload(coresys: CoreSys):
"""Unload AppArmor Profile on host."""
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.agent.apparmor.unload_profile(
Path("/data/apparmor/profile"), Path("/data/apparmor/cache")
)
await coresys.dbus.agent.connect()
assert (
await coresys.dbus.agent.apparmor.unload_profile(
Path("/data/apparmor/profile"), Path("/data/apparmor/cache")
)
is None
)

View File

@ -0,0 +1,20 @@
"""Test CGroup/Agent dbus interface."""
import pytest
from supervisor.coresys import CoreSys
from supervisor.exceptions import DBusNotConnectedError
async def test_dbus_osagent_cgroup_add_devices(coresys: CoreSys):
"""Test wipe data partition on host."""
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.agent.cgroup.add_devices_allowed("9324kl23j4kl", "*:* rwm")
await coresys.dbus.agent.connect()
assert (
await coresys.dbus.agent.cgroup.add_devices_allowed("9324kl23j4kl", "*:* rwm")
is None
)

View File

@ -0,0 +1,28 @@
"""Test Datadisk/Agent dbus interface."""
from pathlib import Path
import pytest
from supervisor.coresys import CoreSys
from supervisor.exceptions import DBusNotConnectedError
async def test_dbus_osagent_datadisk(coresys: CoreSys):
"""Test coresys dbus connection."""
assert coresys.dbus.agent.datadisk.current_device is None
await coresys.dbus.agent.connect()
await coresys.dbus.agent.update()
assert coresys.dbus.agent.datadisk.current_device.as_posix() == "/dev/sda"
async def test_dbus_osagent_datadisk_change_device(coresys: CoreSys):
"""Change datadisk on device."""
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.agent.datadisk.change_device(Path("/dev/sdb"))
await coresys.dbus.agent.connect()
assert await coresys.dbus.agent.datadisk.change_device(Path("/dev/sdb")) is None

View File

@ -0,0 +1,17 @@
"""Test System/Agent dbus interface."""
import pytest
from supervisor.coresys import CoreSys
from supervisor.exceptions import DBusNotConnectedError
async def test_dbus_osagent_system_wipe(coresys: CoreSys):
"""Test wipe data partition on host."""
with pytest.raises(DBusNotConnectedError):
await coresys.dbus.agent.system.schedule_wipe_device()
await coresys.dbus.agent.connect()
assert await coresys.dbus.agent.system.schedule_wipe_device() is None

View File

@ -0,0 +1 @@
"""Tests for DBUS NetworkManager."""

1
tests/fixtures/io_hass_os.json vendored Normal file
View File

@ -0,0 +1 @@
{ "Version": "1.1.0", "Diagnostics": true }

51
tests/fixtures/io_hass_os.xml vendored Normal file
View File

@ -0,0 +1,51 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/io/hass/os">
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="out" type="s" direction="out">
</arg>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="property" type="s" direction="in">
</arg>
<arg name="value" type="v" direction="out">
</arg>
</method>
<method name="GetAll">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="props" type="a{sv}" direction="out">
</arg>
</method>
<method name="Set">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="property" type="s" direction="in">
</arg>
<arg name="value" type="v" direction="in">
</arg>
</method>
<signal name="PropertiesChanged">
<arg name="interface" type="s" direction="out">
</arg>
<arg name="changed_properties" type="a{sv}" direction="out">
</arg>
<arg name="invalidates_properties" type="as" direction="out">
</arg>
</signal>
</interface>
<interface name="io.hass.os">
<property name="Version" type="s" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="invalidates">
</annotation>
</property>
<property name="Diagnostics" type="b" access="readwrite">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true">
</annotation>
</property>
</interface>
</node>

View File

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

View File

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

View File

@ -0,0 +1 @@
{ "ParserVersion": "2.13.2" }

63
tests/fixtures/io_hass_os_AppArmor.xml vendored Normal file
View File

@ -0,0 +1,63 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/io/hass/os/AppArmor">
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="out" type="s" direction="out">
</arg>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="property" type="s" direction="in">
</arg>
<arg name="value" type="v" direction="out">
</arg>
</method>
<method name="GetAll">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="props" type="a{sv}" direction="out">
</arg>
</method>
<method name="Set">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="property" type="s" direction="in">
</arg>
<arg name="value" type="v" direction="in">
</arg>
</method>
<signal name="PropertiesChanged">
<arg name="interface" type="s" direction="out">
</arg>
<arg name="changed_properties" type="a{sv}" direction="out">
</arg>
<arg name="invalidates_properties" type="as" direction="out">
</arg>
</signal>
</interface>
<interface name="io.hass.os.AppArmor">
<method name="LoadProfile">
<arg type="s" direction="in">
</arg>
<arg type="s" direction="in">
</arg>
<arg type="b" direction="out">
</arg>
</method>
<method name="UnloadProfile">
<arg type="s" direction="in">
</arg>
<arg type="s" direction="in">
</arg>
<arg type="b" direction="out">
</arg>
</method>
<property name="ParserVersion" type="s" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="invalidates">
</annotation>
</property>
</interface>
</node>

View File

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

51
tests/fixtures/io_hass_os_CGroup.xml vendored Normal file
View File

@ -0,0 +1,51 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/io/hass/os/CGroup">
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="out" type="s" direction="out">
</arg>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="property" type="s" direction="in">
</arg>
<arg name="value" type="v" direction="out">
</arg>
</method>
<method name="GetAll">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="props" type="a{sv}" direction="out">
</arg>
</method>
<method name="Set">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="property" type="s" direction="in">
</arg>
<arg name="value" type="v" direction="in">
</arg>
</method>
<signal name="PropertiesChanged">
<arg name="interface" type="s" direction="out">
</arg>
<arg name="changed_properties" type="a{sv}" direction="out">
</arg>
<arg name="invalidates_properties" type="as" direction="out">
</arg>
</signal>
</interface>
<interface name="io.hass.os.CGroup">
<method name="AddDevicesAllowed">
<arg type="s" direction="in">
</arg>
<arg type="s" direction="in">
</arg>
<arg type="b" direction="out">
</arg>
</method>
</interface>
</node>

View File

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

View File

@ -0,0 +1 @@
{ "CurrentDevice": "/dev/sda" }

53
tests/fixtures/io_hass_os_DataDisk.xml vendored Normal file
View File

@ -0,0 +1,53 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/io/hass/os/DataDisk">
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="out" type="s" direction="out">
</arg>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="property" type="s" direction="in">
</arg>
<arg name="value" type="v" direction="out">
</arg>
</method>
<method name="GetAll">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="props" type="a{sv}" direction="out">
</arg>
</method>
<method name="Set">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="property" type="s" direction="in">
</arg>
<arg name="value" type="v" direction="in">
</arg>
</method>
<signal name="PropertiesChanged">
<arg name="interface" type="s" direction="out">
</arg>
<arg name="changed_properties" type="a{sv}" direction="out">
</arg>
<arg name="invalidates_properties" type="as" direction="out">
</arg>
</signal>
</interface>
<interface name="io.hass.os.DataDisk">
<method name="ChangeDevice">
<arg type="s" direction="in">
</arg>
<arg type="b" direction="out">
</arg>
</method>
<property name="CurrentDevice" type="s" access="read">
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true">
</annotation>
</property>
</interface>
</node>

View File

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

51
tests/fixtures/io_hass_os_System.xml vendored Normal file
View File

@ -0,0 +1,51 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/io/hass/os/System">
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="out" type="s" direction="out">
</arg>
</method>
</interface>
<interface name="org.freedesktop.DBus.Properties">
<method name="Get">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="property" type="s" direction="in">
</arg>
<arg name="value" type="v" direction="out">
</arg>
</method>
<method name="GetAll">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="props" type="a{sv}" direction="out">
</arg>
</method>
<method name="Set">
<arg name="interface" type="s" direction="in">
</arg>
<arg name="property" type="s" direction="in">
</arg>
<arg name="value" type="v" direction="in">
</arg>
</method>
<signal name="PropertiesChanged">
<arg name="interface" type="s" direction="out">
</arg>
<arg name="changed_properties" type="a{sv}" direction="out">
</arg>
<arg name="invalidates_properties" type="as" direction="out">
</arg>
</signal>
</interface>
<interface name="io.hass.os.System">
<method name="ScheduleWipeDevice">
<arg type="b" direction="out">
</arg>
</method>
<method name="WipeDevice">
<arg type="b" direction="out">
</arg>
</method>
</interface>
</node>

View File

@ -2,8 +2,9 @@
# pylint: disable=import-error,protected-access
from unittest.mock import MagicMock, patch
from supervisor.const import CoreState, HostFeature
from supervisor.const import CoreState
from supervisor.coresys import CoreSys
from supervisor.host.const import HostFeature
from supervisor.resolution.evaluations.systemd import EvaluateSystemd