mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-18 22:56:31 +00:00
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:
parent
7c6c982414
commit
2b6829a786
@ -48,6 +48,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
jq \
|
jq \
|
||||||
dbus \
|
dbus \
|
||||||
network-manager \
|
network-manager \
|
||||||
|
apparmor-utils \
|
||||||
libpulse0 \
|
libpulse0 \
|
||||||
&& bash <(curl https://getvcn.codenotary.com -L) \
|
&& bash <(curl https://getvcn.codenotary.com -L) \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
@ -88,6 +88,21 @@ function init_udev() {
|
|||||||
udevadm trigger && udevadm settle
|
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"
|
echo "Run Supervisor"
|
||||||
|
|
||||||
start_docker
|
start_docker
|
||||||
@ -99,6 +114,7 @@ if [ "$( docker container inspect -f '{{.State.Status}}' hassio_supervisor )" ==
|
|||||||
docker rm -f hassio_supervisor
|
docker rm -f hassio_supervisor
|
||||||
init_dbus
|
init_dbus
|
||||||
init_udev
|
init_udev
|
||||||
|
init_os-agent
|
||||||
cleanup_lastboot
|
cleanup_lastboot
|
||||||
run_supervisor
|
run_supervisor
|
||||||
stop_docker
|
stop_docker
|
||||||
@ -111,6 +127,7 @@ else
|
|||||||
cleanup_docker
|
cleanup_docker
|
||||||
init_dbus
|
init_dbus
|
||||||
init_udev
|
init_udev
|
||||||
|
init_os-agent
|
||||||
run_supervisor
|
run_supervisor
|
||||||
stop_docker
|
stop_docker
|
||||||
fi
|
fi
|
||||||
|
@ -33,7 +33,7 @@ from .const import (
|
|||||||
)
|
)
|
||||||
from .core import Core
|
from .core import Core
|
||||||
from .coresys import CoreSys
|
from .coresys import CoreSys
|
||||||
from .dbus import DBusManager
|
from .dbus.manager import DBusManager
|
||||||
from .discovery import Discovery
|
from .discovery import Discovery
|
||||||
from .hardware.module import HardwareManager
|
from .hardware.module import HardwareManager
|
||||||
from .hassos import HassOS
|
from .hassos import HassOS
|
||||||
|
@ -21,7 +21,7 @@ if TYPE_CHECKING:
|
|||||||
from .arch import CpuArch
|
from .arch import CpuArch
|
||||||
from .auth import Auth
|
from .auth import Auth
|
||||||
from .core import Core
|
from .core import Core
|
||||||
from .dbus import DBusManager
|
from .dbus.manager import DBusManager
|
||||||
from .discovery import Discovery
|
from .discovery import Discovery
|
||||||
from .hardware.module import HardwareManager
|
from .hardware.module import HardwareManager
|
||||||
from .hassos import HassOS
|
from .hassos import HassOS
|
||||||
|
@ -1,84 +1 @@
|
|||||||
"""D-Bus interface objects."""
|
"""D-Bus interfaces."""
|
||||||
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()
|
|
||||||
|
99
supervisor/dbus/agent/__init__.py
Normal file
99
supervisor/dbus/agent/__init__.py
Normal 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()
|
48
supervisor/dbus/agent/apparmor.py
Normal file
48
supervisor/dbus/agent/apparmor.py
Normal 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())
|
19
supervisor/dbus/agent/cgroup.py
Normal file
19
supervisor/dbus/agent/cgroup.py
Normal 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)
|
41
supervisor/dbus/agent/datadisk.py
Normal file
41
supervisor/dbus/agent/datadisk.py
Normal 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())
|
19
supervisor/dbus/agent/system.py
Normal file
19
supervisor/dbus/agent/system.py
Normal 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()
|
@ -20,6 +20,12 @@ DBUS_NAME_NM_CONNECTION_ACTIVE_CHANGED = (
|
|||||||
DBUS_NAME_SYSTEMD = "org.freedesktop.systemd1"
|
DBUS_NAME_SYSTEMD = "org.freedesktop.systemd1"
|
||||||
DBUS_NAME_LOGIND = "org.freedesktop.login1"
|
DBUS_NAME_LOGIND = "org.freedesktop.login1"
|
||||||
DBUS_NAME_TIMEDATE = "org.freedesktop.timedate1"
|
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_BASE = "/"
|
||||||
DBUS_OBJECT_DNS = "/org/freedesktop/NetworkManager/DnsManager"
|
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_SYSTEMD = "/org/freedesktop/systemd1"
|
||||||
DBUS_OBJECT_LOGIND = "/org/freedesktop/login1"
|
DBUS_OBJECT_LOGIND = "/org/freedesktop/login1"
|
||||||
DBUS_OBJECT_TIMEDATE = "/org/freedesktop/timedate1"
|
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_CONNECTIONS = "ActiveConnections"
|
||||||
DBUS_ATTR_ACTIVE_CONNECTION = "ActiveConnection"
|
DBUS_ATTR_ACTIVE_CONNECTION = "ActiveConnection"
|
||||||
@ -77,6 +88,9 @@ DBUS_ATTR_LOCALRTC = "LocalRTC"
|
|||||||
DBUS_ATTR_NTP = "NTP"
|
DBUS_ATTR_NTP = "NTP"
|
||||||
DBUS_ATTR_NTPSYNCHRONIZED = "NTPSynchronized"
|
DBUS_ATTR_NTPSYNCHRONIZED = "NTPSynchronized"
|
||||||
DBUS_ATTR_TIMEUSEC = "TimeUSec"
|
DBUS_ATTR_TIMEUSEC = "TimeUSec"
|
||||||
|
DBUS_ATTR_DIAGNOSTICS = "Diagnostics"
|
||||||
|
DBUS_ATTR_CURRENT_DEVICE = "CurrentDevice"
|
||||||
|
DBUS_ATTR_PARSER_VERSION = "ParserVersion"
|
||||||
|
|
||||||
|
|
||||||
class RaucState(str, Enum):
|
class RaucState(str, Enum):
|
||||||
|
93
supervisor/dbus/manager.py
Normal file
93
supervisor/dbus/manager.py
Normal 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()
|
@ -4,10 +4,10 @@ from functools import lru_cache
|
|||||||
import logging
|
import logging
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from ..const import HostFeature
|
|
||||||
from ..coresys import CoreSys, CoreSysAttributes
|
from ..coresys import CoreSys, CoreSysAttributes
|
||||||
from ..exceptions import HassioError, PulseAudioError
|
from ..exceptions import HassioError, PulseAudioError
|
||||||
from .apparmor import AppArmorControl
|
from .apparmor import AppArmorControl
|
||||||
|
from .const import HostFeature
|
||||||
from .control import SystemControl
|
from .control import SystemControl
|
||||||
from .info import InfoCenter
|
from .info import InfoCenter
|
||||||
from .network import NetworkManager
|
from .network import NetworkManager
|
||||||
@ -85,8 +85,11 @@ class HostManager(CoreSysAttributes):
|
|||||||
if self.sys_dbus.timedate.is_connected:
|
if self.sys_dbus.timedate.is_connected:
|
||||||
features.append(HostFeature.TIMEDATE)
|
features.append(HostFeature.TIMEDATE)
|
||||||
|
|
||||||
|
if self.sys_dbus.agent.is_connected:
|
||||||
|
features.append(HostFeature.AGENT)
|
||||||
|
|
||||||
if self.sys_hassos.available:
|
if self.sys_hassos.available:
|
||||||
features.append(HostFeature.HASSOS)
|
features.append(HostFeature.HAOS)
|
||||||
|
|
||||||
return features
|
return features
|
||||||
|
|
||||||
@ -100,6 +103,9 @@ class HostManager(CoreSysAttributes):
|
|||||||
if self.sys_dbus.network.is_connected:
|
if self.sys_dbus.network.is_connected:
|
||||||
await self.network.update()
|
await self.network.update()
|
||||||
|
|
||||||
|
if self.sys_dbus.agent.is_connected:
|
||||||
|
await self.sys_dbus.agent.update()
|
||||||
|
|
||||||
with suppress(PulseAudioError):
|
with suppress(PulseAudioError):
|
||||||
await self.sound.update()
|
await self.sound.update()
|
||||||
|
|
||||||
|
@ -33,3 +33,16 @@ class WifiMode(str, Enum):
|
|||||||
MESH = "mesh"
|
MESH = "mesh"
|
||||||
ADHOC = "adhoc"
|
ADHOC = "adhoc"
|
||||||
AP = "ap"
|
AP = "ap"
|
||||||
|
|
||||||
|
|
||||||
|
class HostFeature(str, Enum):
|
||||||
|
"""Host feature."""
|
||||||
|
|
||||||
|
HAOS = "haos"
|
||||||
|
HOSTNAME = "hostname"
|
||||||
|
NETWORK = "network"
|
||||||
|
REBOOT = "reboot"
|
||||||
|
SERVICES = "services"
|
||||||
|
SHUTDOWN = "shutdown"
|
||||||
|
AGENT = "agent"
|
||||||
|
TIMEDATE = "timedate"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""A collection of tasks."""
|
"""A collection of tasks."""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from ..const import AddonState, HostFeature
|
from ..const import AddonState
|
||||||
from ..coresys import CoreSysAttributes
|
from ..coresys import CoreSysAttributes
|
||||||
from ..exceptions import (
|
from ..exceptions import (
|
||||||
AddonsError,
|
AddonsError,
|
||||||
@ -12,6 +12,7 @@ from ..exceptions import (
|
|||||||
MulticastError,
|
MulticastError,
|
||||||
ObserverError,
|
ObserverError,
|
||||||
)
|
)
|
||||||
|
from ..host.const import HostFeature
|
||||||
from ..jobs.decorator import Job, JobCondition
|
from ..jobs.decorator import Job, JobCondition
|
||||||
|
|
||||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
"""Evaluation class for network manager."""
|
"""Evaluation class for network manager."""
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from ...const import CoreState, HostFeature
|
from ...const import CoreState
|
||||||
from ...coresys import CoreSys
|
from ...coresys import CoreSys
|
||||||
|
from ...host.const import HostFeature
|
||||||
from ..const import UnsupportedReason
|
from ..const import UnsupportedReason
|
||||||
from .base import EvaluateBase
|
from .base import EvaluateBase
|
||||||
|
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
"""Evaluation class for systemd."""
|
"""Evaluation class for systemd."""
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from ...const import CoreState, HostFeature
|
from ...const import CoreState
|
||||||
from ...coresys import CoreSys
|
from ...coresys import CoreSys
|
||||||
|
from ...host.const import HostFeature
|
||||||
from ..const import UnsupportedReason
|
from ..const import UnsupportedReason
|
||||||
from .base import EvaluateBase
|
from .base import EvaluateBase
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@ MONITOR: str = "gdbus monitor --system --dest {bus}"
|
|||||||
WAIT: str = "gdbus wait --system --activate {bus} --timeout 5 {bus}"
|
WAIT: str = "gdbus wait --system --activate {bus} --timeout 5 {bus}"
|
||||||
|
|
||||||
DBUS_METHOD_GETALL: str = "org.freedesktop.DBus.Properties.GetAll"
|
DBUS_METHOD_GETALL: str = "org.freedesktop.DBus.Properties.GetAll"
|
||||||
|
DBUS_METHOD_SET: str = "org.freedesktop.DBus.Properties.Set"
|
||||||
|
|
||||||
|
|
||||||
def _convert_bytes(value: str) -> str:
|
def _convert_bytes(value: str) -> str:
|
||||||
@ -225,6 +226,16 @@ class DBus:
|
|||||||
_LOGGER.error("No attributes returned for %s", interface)
|
_LOGGER.error("No attributes returned for %s", interface)
|
||||||
raise DBusFatalError() from err
|
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:
|
async def _send(self, command: List[str], silent=False) -> str:
|
||||||
"""Send command over dbus."""
|
"""Send command over dbus."""
|
||||||
# Run command
|
# Run command
|
||||||
|
1
tests/dbus/agent/__init__.py
Normal file
1
tests/dbus/agent/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Tests for OS-Agent."""
|
15
tests/dbus/agent/test_agent.py
Normal file
15
tests/dbus/agent/test_agent.py
Normal 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
|
53
tests/dbus/agent/test_apparmor.py
Normal file
53
tests/dbus/agent/test_apparmor.py
Normal 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
|
||||||
|
)
|
20
tests/dbus/agent/test_cgroup.py
Normal file
20
tests/dbus/agent/test_cgroup.py
Normal 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
|
||||||
|
)
|
28
tests/dbus/agent/test_datadisk.py
Normal file
28
tests/dbus/agent/test_datadisk.py
Normal 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
|
17
tests/dbus/agent/test_system.py
Normal file
17
tests/dbus/agent/test_system.py
Normal 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
|
1
tests/dbus/network/__init__.py
Normal file
1
tests/dbus/network/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
"""Tests for DBUS NetworkManager."""
|
1
tests/fixtures/io_hass_os.json
vendored
Normal file
1
tests/fixtures/io_hass_os.json
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ "Version": "1.1.0", "Diagnostics": true }
|
51
tests/fixtures/io_hass_os.xml
vendored
Normal file
51
tests/fixtures/io_hass_os.xml
vendored
Normal 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>
|
1
tests/fixtures/io_hass_os_AppArmor-LoadProfile.fixture
vendored
Normal file
1
tests/fixtures/io_hass_os_AppArmor-LoadProfile.fixture
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(<true>,)
|
1
tests/fixtures/io_hass_os_AppArmor-UnloadProfile.fixture
vendored
Normal file
1
tests/fixtures/io_hass_os_AppArmor-UnloadProfile.fixture
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(<true>,)
|
1
tests/fixtures/io_hass_os_AppArmor.json
vendored
Normal file
1
tests/fixtures/io_hass_os_AppArmor.json
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ "ParserVersion": "2.13.2" }
|
63
tests/fixtures/io_hass_os_AppArmor.xml
vendored
Normal file
63
tests/fixtures/io_hass_os_AppArmor.xml
vendored
Normal 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>
|
1
tests/fixtures/io_hass_os_CGroup-AddDevicesAllowed.fixture
vendored
Normal file
1
tests/fixtures/io_hass_os_CGroup-AddDevicesAllowed.fixture
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(<true>,)
|
51
tests/fixtures/io_hass_os_CGroup.xml
vendored
Normal file
51
tests/fixtures/io_hass_os_CGroup.xml
vendored
Normal 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>
|
1
tests/fixtures/io_hass_os_DataDisk-ChangeDevice.fixture
vendored
Normal file
1
tests/fixtures/io_hass_os_DataDisk-ChangeDevice.fixture
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(<true>,)
|
1
tests/fixtures/io_hass_os_DataDisk.json
vendored
Normal file
1
tests/fixtures/io_hass_os_DataDisk.json
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
{ "CurrentDevice": "/dev/sda" }
|
53
tests/fixtures/io_hass_os_DataDisk.xml
vendored
Normal file
53
tests/fixtures/io_hass_os_DataDisk.xml
vendored
Normal 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>
|
1
tests/fixtures/io_hass_os_System-ScheduleWipeDevice.fixture
vendored
Normal file
1
tests/fixtures/io_hass_os_System-ScheduleWipeDevice.fixture
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
(<true>,)
|
51
tests/fixtures/io_hass_os_System.xml
vendored
Normal file
51
tests/fixtures/io_hass_os_System.xml
vendored
Normal 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>
|
@ -2,8 +2,9 @@
|
|||||||
# pylint: disable=import-error,protected-access
|
# pylint: disable=import-error,protected-access
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from supervisor.const import CoreState, HostFeature
|
from supervisor.const import CoreState
|
||||||
from supervisor.coresys import CoreSys
|
from supervisor.coresys import CoreSys
|
||||||
|
from supervisor.host.const import HostFeature
|
||||||
from supervisor.resolution.evaluations.systemd import EvaluateSystemd
|
from supervisor.resolution.evaluations.systemd import EvaluateSystemd
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user