mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-24 09:36:31 +00:00
commit
860442d5c4
@ -6,7 +6,7 @@
|
||||
"appPort": "9123:8123",
|
||||
"runArgs": [
|
||||
"-e",
|
||||
"GIT_EDITOR='code --wait'",
|
||||
"GIT_EDITOR=\"code --wait\"",
|
||||
"--privileged"
|
||||
],
|
||||
"extensions": [
|
||||
@ -26,4 +26,4 @@
|
||||
"editor.formatOnType": true,
|
||||
"files.trimTrailingWhitespace": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
3
API.md
3
API.md
@ -753,7 +753,8 @@ return:
|
||||
"host": "ip-address",
|
||||
"version": "1",
|
||||
"latest_version": "2",
|
||||
"servers": ["dns://8.8.8.8"]
|
||||
"servers": ["dns://8.8.8.8"],
|
||||
"locals": ["dns://xy"]
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -6,7 +6,7 @@ import sys
|
||||
|
||||
from hassio import bootstrap
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def initialize_event_loop():
|
||||
|
@ -19,7 +19,7 @@ from ..store.addon import AddonStore
|
||||
from .addon import Addon
|
||||
from .data import AddonsData
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
AnyAddon = Union[Addon, AddonStore]
|
||||
|
||||
|
@ -55,7 +55,7 @@ from .model import AddonModel, Data
|
||||
from .utils import remove_data
|
||||
from .validate import SCHEMA_ADDON_SNAPSHOT, validate_options
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
RE_WEBUI = re.compile(
|
||||
r"^(?:(?P<s_prefix>https?)|\[PROTO:(?P<t_proto>\w+)\])"
|
||||
|
@ -17,7 +17,7 @@ from ..store.addon import AddonStore
|
||||
from .addon import Addon
|
||||
from .validate import SCHEMA_ADDONS_FILE
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
Config = Dict[str, Any]
|
||||
|
||||
|
@ -22,7 +22,7 @@ from ..const import (
|
||||
if TYPE_CHECKING:
|
||||
from .model import AddonModel
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def rating_security(addon: AddonModel) -> int:
|
||||
|
@ -95,7 +95,7 @@ from ..validate import (
|
||||
UUID_MATCH,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
RE_VOLUME = re.compile(r"^(config|ssl|addons|backup|share)(?::(rw|ro))?$")
|
||||
|
@ -22,7 +22,7 @@ from .services import APIServices
|
||||
from .snapshots import APISnapshots
|
||||
from .supervisor import APISupervisor
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RestAPI(CoreSysAttributes):
|
||||
|
@ -94,7 +94,7 @@ from ..exceptions import APIError
|
||||
from ..validate import ALSA_DEVICE, DOCKER_PORTS
|
||||
from .utils import api_process, api_process_raw, api_validate
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
SCHEMA_VERSION = vol.Schema({vol.Optional(ATTR_VERSION): vol.Coerce(str)})
|
||||
|
||||
|
@ -10,7 +10,7 @@ from ..const import REQUEST_FROM, CONTENT_TYPE_JSON, CONTENT_TYPE_URL
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import APIForbidden
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APIAuth(CoreSysAttributes):
|
||||
|
@ -12,9 +12,10 @@ from ..const import (
|
||||
ATTR_CPU_PERCENT,
|
||||
ATTR_HOST,
|
||||
ATTR_LATEST_VERSION,
|
||||
ATTR_LOCALS,
|
||||
ATTR_MEMORY_LIMIT,
|
||||
ATTR_MEMORY_USAGE,
|
||||
ATTR_MEMORY_PERCENT,
|
||||
ATTR_MEMORY_USAGE,
|
||||
ATTR_NETWORK_RX,
|
||||
ATTR_NETWORK_TX,
|
||||
ATTR_SERVERS,
|
||||
@ -26,7 +27,7 @@ from ..exceptions import APIError
|
||||
from ..validate import DNS_SERVER_LIST
|
||||
from .utils import api_process, api_process_raw, api_validate
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
# pylint: disable=no-value-for-parameter
|
||||
SCHEMA_OPTIONS = vol.Schema({vol.Optional(ATTR_SERVERS): DNS_SERVER_LIST})
|
||||
@ -45,6 +46,7 @@ class APICoreDNS(CoreSysAttributes):
|
||||
ATTR_LATEST_VERSION: self.sys_dns.latest_version,
|
||||
ATTR_HOST: str(self.sys_docker.network.dns),
|
||||
ATTR_SERVERS: self.sys_dns.servers,
|
||||
ATTR_LOCALS: self.sys_host.network.dns_servers,
|
||||
}
|
||||
|
||||
@api_process
|
||||
|
@ -12,7 +12,7 @@ from ..const import (
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APIHardware(CoreSysAttributes):
|
||||
|
@ -16,7 +16,7 @@ from ..const import (
|
||||
from ..coresys import CoreSysAttributes
|
||||
from .utils import api_process, api_validate
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
SCHEMA_VERSION = vol.Schema({vol.Optional(ATTR_VERSION): vol.Coerce(str)})
|
||||
|
||||
|
@ -36,7 +36,7 @@ from ..exceptions import APIError
|
||||
from ..validate import DOCKER_IMAGE, NETWORK_PORT
|
||||
from .utils import api_process, api_process_raw, api_validate
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
# pylint: disable=no-value-for-parameter
|
||||
SCHEMA_OPTIONS = vol.Schema(
|
||||
|
@ -20,7 +20,7 @@ from ..const import (
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
SERVICE = "service"
|
||||
|
||||
|
@ -19,7 +19,7 @@ from ..const import (
|
||||
from ..coresys import CoreSysAttributes
|
||||
from .utils import api_process
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APIInfo(CoreSysAttributes):
|
||||
|
@ -28,7 +28,7 @@ from ..const import (
|
||||
from ..coresys import CoreSysAttributes
|
||||
from .utils import api_process
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APIIngress(CoreSysAttributes):
|
||||
|
@ -14,7 +14,7 @@ from ..const import HEADER_HA_ACCESS
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import HomeAssistantAuthError, HomeAssistantAPIError, APIError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class APIProxy(CoreSysAttributes):
|
||||
|
@ -16,7 +16,7 @@ from ..const import (
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
# fmt: off
|
||||
|
||||
|
@ -28,7 +28,7 @@ from ..const import (
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import APIError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# pylint: disable=no-value-for-parameter
|
||||
|
@ -44,7 +44,7 @@ from ..utils.validate import validate_timezone
|
||||
from ..validate import CHANNELS, LOG_LEVEL, REPOSITORIES, WAIT_BOOT
|
||||
from .utils import api_process, api_process_raw, api_validate
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
# pylint: disable=no-value-for-parameter
|
||||
SCHEMA_OPTIONS = vol.Schema(
|
||||
|
@ -16,7 +16,7 @@ from ..const import (
|
||||
)
|
||||
from ..exceptions import HassioError, APIError, APIForbidden
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def json_loads(data):
|
||||
|
@ -8,7 +8,7 @@ from .coresys import CoreSys, CoreSysAttributes
|
||||
from .exceptions import HassioArchNotFound, JsonFileError
|
||||
from .utils.json import read_json_file
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
ARCH_JSON: Path = Path(__file__).parent.joinpath("data/arch.json")
|
||||
|
||||
|
@ -8,7 +8,7 @@ from .utils.json import JsonConfig
|
||||
from .validate import SCHEMA_AUTH_CONFIG
|
||||
from .exceptions import AuthError, HomeAssistantAPIError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Auth(JsonConfig, CoreSysAttributes):
|
||||
|
@ -29,7 +29,7 @@ from .tasks import Tasks
|
||||
from .updater import Updater
|
||||
from .utils.dt import fetch_timezone
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
ENV_SHARE = "SUPERVISOR_SHARE"
|
||||
ENV_NAME = "SUPERVISOR_NAME"
|
||||
|
@ -19,7 +19,7 @@ from .utils.dt import parse_datetime
|
||||
from .utils.json import JsonConfig
|
||||
from .validate import SCHEMA_HASSIO_CONFIG
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
HOMEASSISTANT_CONFIG = PurePath("homeassistant")
|
||||
|
||||
|
@ -3,7 +3,7 @@ from pathlib import Path
|
||||
from ipaddress import ip_network
|
||||
|
||||
|
||||
HASSIO_VERSION = "181"
|
||||
HASSIO_VERSION = "182"
|
||||
|
||||
URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons"
|
||||
URL_HASSIO_VERSION = "https://version.home-assistant.io/{channel}.json"
|
||||
@ -218,6 +218,7 @@ ATTR_DEBUG = "debug"
|
||||
ATTR_DEBUG_BLOCK = "debug_block"
|
||||
ATTR_DNS = "dns"
|
||||
ATTR_SERVERS = "servers"
|
||||
ATTR_LOCALS = "locals"
|
||||
ATTR_UDEV = "udev"
|
||||
|
||||
PROVIDE_SERVICE = "provide"
|
||||
|
@ -14,7 +14,7 @@ from .const import (
|
||||
)
|
||||
from .exceptions import HassioError, HomeAssistantError, SupervisorUpdateError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HassIO(CoreSysAttributes):
|
||||
@ -30,15 +30,15 @@ class HassIO(CoreSysAttributes):
|
||||
|
||||
async def setup(self):
|
||||
"""Setup HassIO orchestration."""
|
||||
# Load CoreDNS
|
||||
await self.sys_dns.load()
|
||||
|
||||
# Load DBus
|
||||
await self.sys_dbus.load()
|
||||
|
||||
# Load Host
|
||||
await self.sys_host.load()
|
||||
|
||||
# Load CoreDNS
|
||||
await self.sys_dns.load()
|
||||
|
||||
# Load Home Assistant
|
||||
await self.sys_homeassistant.load()
|
||||
|
||||
|
@ -1,39 +1,57 @@
|
||||
"""D-Bus interface objects."""
|
||||
import logging
|
||||
|
||||
from .systemd import Systemd
|
||||
from .hostname import Hostname
|
||||
from .rauc import Rauc
|
||||
from ..coresys import CoreSysAttributes
|
||||
from .nmi_dns import NMIDnsManager
|
||||
from ..coresys import CoreSysAttributes, CoreSys
|
||||
from ..exceptions import DBusNotConnectedError
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DBusManager(CoreSysAttributes):
|
||||
"""A DBus Interface handler."""
|
||||
|
||||
def __init__(self, coresys):
|
||||
def __init__(self, coresys: CoreSys) -> None:
|
||||
"""Initialize D-Bus interface."""
|
||||
self.coresys = coresys
|
||||
self.coresys: CoreSys = coresys
|
||||
|
||||
self._systemd = Systemd()
|
||||
self._hostname = Hostname()
|
||||
self._rauc = Rauc()
|
||||
self._systemd: Systemd = Systemd()
|
||||
self._hostname: Hostname = Hostname()
|
||||
self._rauc: Rauc = Rauc()
|
||||
self._nmi_dns: NMIDnsManager = NMIDnsManager()
|
||||
|
||||
@property
|
||||
def systemd(self):
|
||||
def systemd(self) -> Systemd:
|
||||
"""Return the systemd interface."""
|
||||
return self._systemd
|
||||
|
||||
@property
|
||||
def hostname(self):
|
||||
def hostname(self) -> Hostname:
|
||||
"""Return the hostname interface."""
|
||||
return self._hostname
|
||||
|
||||
@property
|
||||
def rauc(self):
|
||||
def rauc(self) -> Rauc:
|
||||
"""Return the rauc interface."""
|
||||
return self._rauc
|
||||
|
||||
async def load(self):
|
||||
@property
|
||||
def nmi_dns(self) -> NMIDnsManager:
|
||||
"""Return NetworkManager DNS interface."""
|
||||
return self._nmi_dns
|
||||
|
||||
async def load(self) -> None:
|
||||
"""Connect interfaces to D-Bus."""
|
||||
await self.systemd.connect()
|
||||
await self.hostname.connect()
|
||||
await self.rauc.connect()
|
||||
|
||||
try:
|
||||
await self.systemd.connect()
|
||||
await self.hostname.connect()
|
||||
await self.rauc.connect()
|
||||
await self.nmi_dns.connect()
|
||||
except DBusNotConnectedError:
|
||||
_LOGGER.error(
|
||||
"No DBus support from Host. Disabled any kind of host control!"
|
||||
)
|
||||
|
@ -1,12 +1,13 @@
|
||||
"""D-Bus interface for hostname."""
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from .interface import DBusInterface
|
||||
from .utils import dbus_connected
|
||||
from ..exceptions import DBusError
|
||||
from ..exceptions import DBusError, DBusInterfaceError
|
||||
from ..utils.gdbus import DBus
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
DBUS_NAME = "org.freedesktop.hostname1"
|
||||
DBUS_OBJECT = "/org/freedesktop/hostname1"
|
||||
@ -15,12 +16,55 @@ DBUS_OBJECT = "/org/freedesktop/hostname1"
|
||||
class Hostname(DBusInterface):
|
||||
"""Handle D-Bus interface for hostname/system."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize Properties."""
|
||||
self._hostname: Optional[str] = None
|
||||
self._chassis: Optional[str] = None
|
||||
self._deployment: Optional[str] = None
|
||||
self._kernel: Optional[str] = None
|
||||
self._operating_system: Optional[str] = None
|
||||
self._cpe: Optional[str] = None
|
||||
|
||||
async def connect(self):
|
||||
"""Connect to system's D-Bus."""
|
||||
try:
|
||||
self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT)
|
||||
except DBusError:
|
||||
_LOGGER.warning("Can't connect to hostname")
|
||||
except DBusInterfaceError:
|
||||
_LOGGER.warning(
|
||||
"No hostname support on the host. Hostname functions have been disabled."
|
||||
)
|
||||
|
||||
@property
|
||||
def hostname(self) -> Optional[str]:
|
||||
"""Return local hostname."""
|
||||
return self._hostname
|
||||
|
||||
@property
|
||||
def chassis(self) -> Optional[str]:
|
||||
"""Return local chassis type."""
|
||||
return self._chassis
|
||||
|
||||
@property
|
||||
def deployment(self) -> Optional[str]:
|
||||
"""Return local deployment type."""
|
||||
return self._deployment
|
||||
|
||||
@property
|
||||
def kernel(self) -> Optional[str]:
|
||||
"""Return local kernel version."""
|
||||
return self._kernel
|
||||
|
||||
@property
|
||||
def operating_system(self) -> Optional[str]:
|
||||
"""Return local operating system."""
|
||||
return self._operating_system
|
||||
|
||||
@property
|
||||
def cpe(self) -> Optional[str]:
|
||||
"""Return local CPE."""
|
||||
return self._cpe
|
||||
|
||||
@dbus_connected
|
||||
def set_static_hostname(self, hostname):
|
||||
@ -31,9 +75,16 @@ class Hostname(DBusInterface):
|
||||
return self.dbus.SetStaticHostname(hostname, False)
|
||||
|
||||
@dbus_connected
|
||||
def get_properties(self):
|
||||
"""Return local host informations.
|
||||
async def update(self):
|
||||
"""Update Properties."""
|
||||
data = await self.dbus.get_properties(DBUS_NAME)
|
||||
if not data:
|
||||
_LOGGER.warning("Can't get properties for Hostname")
|
||||
return
|
||||
|
||||
Return a coroutine.
|
||||
"""
|
||||
return self.dbus.get_properties(DBUS_NAME)
|
||||
self._hostname = data.get("StaticHostname")
|
||||
self._chassis = data.get("Chassis")
|
||||
self._deployment = data.get("Deployment")
|
||||
self._kernel = data.get("KernelRelease")
|
||||
self._operating_system = data.get("OperatingSystemPrettyName")
|
||||
self._cpe = data.get("OperatingSystemCPEName")
|
||||
|
@ -1,12 +1,13 @@
|
||||
"""Interface class for D-Bus wrappers."""
|
||||
from typing import Optional
|
||||
|
||||
from ..utils.gdbus import DBus
|
||||
|
||||
|
||||
class DBusInterface:
|
||||
"""Handle D-Bus interface for hostname/system."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize systemd."""
|
||||
self.dbus = None
|
||||
dbus: Optional[DBus] = None
|
||||
|
||||
@property
|
||||
def is_connected(self):
|
||||
|
85
hassio/dbus/nmi_dns.py
Normal file
85
hassio/dbus/nmi_dns.py
Normal file
@ -0,0 +1,85 @@
|
||||
"""D-Bus interface for hostname."""
|
||||
import logging
|
||||
from typing import Optional, List
|
||||
|
||||
import attr
|
||||
|
||||
from .interface import DBusInterface
|
||||
from .utils import dbus_connected
|
||||
from ..exceptions import DBusError, DBusInterfaceError
|
||||
from ..utils.gdbus import DBus
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
DBUS_NAME = "org.freedesktop.NetworkManager"
|
||||
DBUS_OBJECT = "/org/freedesktop/NetworkManager/DnsManager"
|
||||
|
||||
|
||||
@attr.s
|
||||
class DNSConfiguration:
|
||||
"""NMI DnsManager configuration Object."""
|
||||
|
||||
nameservers: List[str] = attr.ib()
|
||||
domains: List[str] = attr.ib()
|
||||
interface: str = attr.ib()
|
||||
priority: int = attr.ib()
|
||||
vpn: bool = attr.ib()
|
||||
|
||||
|
||||
class NMIDnsManager(DBusInterface):
|
||||
"""Handle D-Bus interface for NMI DnsManager."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize Properties."""
|
||||
self._mode: Optional[str] = None
|
||||
self._rc_manager: Optional[str] = None
|
||||
self._configuration: List[DNSConfiguration] = []
|
||||
|
||||
@property
|
||||
def mode(self) -> Optional[str]:
|
||||
"""Return Propertie mode."""
|
||||
return self._mode
|
||||
|
||||
@property
|
||||
def rc_manager(self) -> Optional[str]:
|
||||
"""Return Propertie RcManager."""
|
||||
return self._rc_manager
|
||||
|
||||
@property
|
||||
def configuration(self) -> List[DNSConfiguration]:
|
||||
"""Return Propertie configuraton."""
|
||||
return self._configuration
|
||||
|
||||
async def connect(self) -> None:
|
||||
"""Connect to system's D-Bus."""
|
||||
try:
|
||||
self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT)
|
||||
except DBusError:
|
||||
_LOGGER.warning("Can't connect to DnsManager")
|
||||
except DBusInterfaceError:
|
||||
_LOGGER.warning(
|
||||
"No DnsManager support on the host. Local DNS functions have been disabled."
|
||||
)
|
||||
|
||||
@dbus_connected
|
||||
async def update(self):
|
||||
"""Update Properties."""
|
||||
data = await self.dbus.get_properties(f"{DBUS_NAME}.DnsManager")
|
||||
if not data:
|
||||
_LOGGER.warning("Can't get properties for NMI DnsManager")
|
||||
return
|
||||
|
||||
self._mode = data.get("Mode")
|
||||
self._rc_manager = data.get("RcManager")
|
||||
|
||||
# Parse configuraton
|
||||
self._configuration.clear()
|
||||
for config in data.get("Configuration", []):
|
||||
dns = DNSConfiguration(
|
||||
config.get("nameservers"),
|
||||
config.get("domains"),
|
||||
config.get("interface"),
|
||||
config.get("priority"),
|
||||
config.get("vpn"),
|
||||
)
|
||||
self._configuration.append(dns)
|
@ -3,10 +3,10 @@ import logging
|
||||
|
||||
from .interface import DBusInterface
|
||||
from .utils import dbus_connected
|
||||
from ..exceptions import DBusError
|
||||
from ..exceptions import DBusError, DBusInterfaceError
|
||||
from ..utils.gdbus import DBus
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
DBUS_NAME = "de.pengutronix.rauc"
|
||||
DBUS_OBJECT = "/"
|
||||
@ -21,6 +21,8 @@ class Rauc(DBusInterface):
|
||||
self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT)
|
||||
except DBusError:
|
||||
_LOGGER.warning("Can't connect to rauc")
|
||||
except DBusInterfaceError:
|
||||
_LOGGER.warning("Host has no rauc support. OTA updates have been disabled.")
|
||||
|
||||
@dbus_connected
|
||||
def install(self, raucb_file):
|
||||
|
@ -3,10 +3,10 @@ import logging
|
||||
|
||||
from .interface import DBusInterface
|
||||
from .utils import dbus_connected
|
||||
from ..exceptions import DBusError
|
||||
from ..exceptions import DBusError, DBusInterfaceError
|
||||
from ..utils.gdbus import DBus
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
DBUS_NAME = "org.freedesktop.systemd1"
|
||||
DBUS_OBJECT = "/org/freedesktop/systemd1"
|
||||
@ -21,6 +21,10 @@ class Systemd(DBusInterface):
|
||||
self.dbus = await DBus.connect(DBUS_NAME, DBUS_OBJECT)
|
||||
except DBusError:
|
||||
_LOGGER.warning("Can't connect to systemd")
|
||||
except DBusInterfaceError:
|
||||
_LOGGER.warning(
|
||||
"No systemd support on the host. Host control has been disabled."
|
||||
)
|
||||
|
||||
@dbus_connected
|
||||
def reboot(self):
|
||||
|
@ -19,7 +19,7 @@ from .validate import SCHEMA_DISCOVERY_CONFIG, valid_discovery_config
|
||||
if TYPE_CHECKING:
|
||||
from ..addons.addon import Addon
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
CMD_NEW = "post"
|
||||
CMD_DEL = "delete"
|
||||
|
@ -1,24 +1,25 @@
|
||||
"""Home Assistant control object."""
|
||||
import asyncio
|
||||
import logging
|
||||
from contextlib import suppress
|
||||
from ipaddress import IPv4Address
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from string import Template
|
||||
from typing import Awaitable, List, Optional
|
||||
|
||||
import attr
|
||||
import voluptuous as vol
|
||||
|
||||
from .const import ATTR_SERVERS, ATTR_VERSION, DNS_SERVERS, FILE_HASSIO_DNS, DNS_SUFFIX
|
||||
from .const import ATTR_SERVERS, ATTR_VERSION, DNS_SERVERS, DNS_SUFFIX, FILE_HASSIO_DNS
|
||||
from .coresys import CoreSys, CoreSysAttributes
|
||||
from .docker.dns import DockerDNS
|
||||
from .docker.stats import DockerStats
|
||||
from .exceptions import CoreDNSError, CoreDNSUpdateError, DockerAPIError
|
||||
from .misc.forwarder import DNSForward
|
||||
from .utils.json import JsonConfig
|
||||
from .validate import SCHEMA_DNS_CONFIG
|
||||
from .validate import DNS_URL, SCHEMA_DNS_CONFIG
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
COREDNS_TMPL: Path = Path(__file__).parents[0].joinpath("data/coredns.tmpl")
|
||||
RESOLV_CONF: Path = Path("/etc/resolv.conf")
|
||||
@ -212,8 +213,17 @@ class CoreDNS(JsonConfig, CoreSysAttributes):
|
||||
_LOGGER.error("Can't read coredns template file: %s", err)
|
||||
raise CoreDNSError() from None
|
||||
|
||||
# Prepare DNS serverlist: Prio 1 Local, Prio 2 Manual, Prio 3 Fallback
|
||||
dns_servers = []
|
||||
for server in self.sys_host.network.dns_servers + self.servers + DNS_SERVERS:
|
||||
try:
|
||||
DNS_URL(server)
|
||||
if server not in dns_servers:
|
||||
dns_servers.append(server)
|
||||
except vol.Invalid:
|
||||
_LOGGER.warning("Ignore invalid DNS Server: %s", server)
|
||||
|
||||
# Generate config file
|
||||
dns_servers = self.servers + list(set(DNS_SERVERS) - set(self.servers))
|
||||
data = corefile_template.safe_substitute(servers=" ".join(dns_servers))
|
||||
|
||||
try:
|
||||
|
@ -11,7 +11,7 @@ from ..const import SOCKET_DOCKER, DNS_SUFFIX
|
||||
from ..exceptions import DockerAPIError
|
||||
from .network import DockerNetwork
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@attr.s(frozen=True)
|
||||
|
@ -32,7 +32,7 @@ if TYPE_CHECKING:
|
||||
from ..addons.addon import Addon
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
AUDIO_DEVICE = "/dev/snd:/dev/snd:rwm"
|
||||
NO_ADDDRESS = ip_address("0.0.0.0")
|
||||
|
@ -7,7 +7,7 @@ from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import DockerAPIError
|
||||
from .interface import DockerInterface
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
DNS_DOCKER_NAME: str = "hassio_dns"
|
||||
|
||||
|
@ -6,7 +6,7 @@ import docker
|
||||
from ..coresys import CoreSysAttributes
|
||||
from .interface import DockerInterface
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DockerHassOSCli(DockerInterface, CoreSysAttributes):
|
||||
|
@ -10,7 +10,7 @@ from ..const import ENV_TIME, ENV_TOKEN, LABEL_MACHINE
|
||||
from ..exceptions import DockerAPIError
|
||||
from .interface import CommandReturn, DockerInterface
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
HASS_DOCKER_NAME = "homeassistant"
|
||||
|
||||
|
@ -13,7 +13,7 @@ from ..exceptions import DockerAPIError
|
||||
from ..utils import process_lock
|
||||
from .stats import DockerStats
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DockerInterface(CoreSysAttributes):
|
||||
|
@ -8,7 +8,7 @@ import docker
|
||||
from ..const import DOCKER_NETWORK, DOCKER_NETWORK_MASK, DOCKER_NETWORK_RANGE
|
||||
from ..exceptions import DockerAPIError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DockerNetwork:
|
||||
|
@ -10,7 +10,7 @@ from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import DockerAPIError
|
||||
from .interface import DockerInterface
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DockerSupervisor(DockerInterface, CoreSysAttributes):
|
||||
|
@ -149,6 +149,10 @@ class DBusNotConnectedError(HostNotSupportedError):
|
||||
"""DBus is not connected and call a method."""
|
||||
|
||||
|
||||
class DBusInterfaceError(HassioNotSupportedError):
|
||||
"""DBus interface not connected."""
|
||||
|
||||
|
||||
class DBusFatalError(DBusError):
|
||||
"""DBus call going wrong."""
|
||||
|
||||
|
@ -18,7 +18,7 @@ from .exceptions import (
|
||||
DockerAPIError,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HassOS(CoreSysAttributes):
|
||||
|
@ -47,7 +47,7 @@ from .utils import check_port, convert_to_ascii, process_lock
|
||||
from .utils.json import JsonConfig
|
||||
from .validate import SCHEMA_HASS_CONFIG
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
RE_YAML_ERROR = re.compile(r"homeassistant\.util\.yaml")
|
||||
|
||||
|
@ -7,6 +7,7 @@ from .apparmor import AppArmorControl
|
||||
from .control import SystemControl
|
||||
from .info import InfoCenter
|
||||
from .services import ServiceManager
|
||||
from .network import NetworkManager
|
||||
from ..const import (
|
||||
FEATURES_REBOOT,
|
||||
FEATURES_SHUTDOWN,
|
||||
@ -14,49 +15,56 @@ from ..const import (
|
||||
FEATURES_SERVICES,
|
||||
FEATURES_HASSOS,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..coresys import CoreSysAttributes, CoreSys
|
||||
from ..exceptions import HassioError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HostManager(CoreSysAttributes):
|
||||
"""Manage supported function from host."""
|
||||
|
||||
def __init__(self, coresys):
|
||||
def __init__(self, coresys: CoreSys):
|
||||
"""Initialize Host manager."""
|
||||
self.coresys = coresys
|
||||
self._alsa = AlsaAudio(coresys)
|
||||
self._apparmor = AppArmorControl(coresys)
|
||||
self._control = SystemControl(coresys)
|
||||
self._info = InfoCenter(coresys)
|
||||
self._services = ServiceManager(coresys)
|
||||
self.coresys: CoreSys = coresys
|
||||
|
||||
self._alsa: AlsaAudio = AlsaAudio(coresys)
|
||||
self._apparmor: AppArmorControl = AppArmorControl(coresys)
|
||||
self._control: SystemControl = SystemControl(coresys)
|
||||
self._info: InfoCenter = InfoCenter(coresys)
|
||||
self._services: ServiceManager = ServiceManager(coresys)
|
||||
self._network: NetworkManager = NetworkManager(coresys)
|
||||
|
||||
@property
|
||||
def alsa(self):
|
||||
def alsa(self) -> AlsaAudio:
|
||||
"""Return host ALSA handler."""
|
||||
return self._alsa
|
||||
|
||||
@property
|
||||
def apparmor(self):
|
||||
def apparmor(self) -> AppArmorControl:
|
||||
"""Return host AppArmor handler."""
|
||||
return self._apparmor
|
||||
|
||||
@property
|
||||
def control(self):
|
||||
def control(self) -> SystemControl:
|
||||
"""Return host control handler."""
|
||||
return self._control
|
||||
|
||||
@property
|
||||
def info(self):
|
||||
def info(self) -> InfoCenter:
|
||||
"""Return host info handler."""
|
||||
return self._info
|
||||
|
||||
@property
|
||||
def services(self):
|
||||
def services(self) -> ServiceManager:
|
||||
"""Return host services handler."""
|
||||
return self._services
|
||||
|
||||
@property
|
||||
def network(self) -> NetworkManager:
|
||||
"""Return host NetworkManager handler."""
|
||||
return self._network
|
||||
|
||||
@property
|
||||
def supperted_features(self):
|
||||
"""Return a list of supported host features."""
|
||||
@ -81,6 +89,9 @@ class HostManager(CoreSysAttributes):
|
||||
if self.sys_dbus.systemd.is_connected:
|
||||
await self.services.update()
|
||||
|
||||
if self.sys_dbus.nmi_dns.is_connected:
|
||||
await self.network.update()
|
||||
|
||||
async def load(self):
|
||||
"""Load host information."""
|
||||
with suppress(HassioError):
|
||||
|
@ -9,7 +9,7 @@ import attr
|
||||
from ..const import ATTR_INPUT, ATTR_OUTPUT, ATTR_DEVICES, ATTR_NAME, CHAN_ID, CHAN_TYPE
|
||||
from ..coresys import CoreSysAttributes
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
DefaultConfig = attr.make_class("DefaultConfig", ["input", "output"])
|
||||
|
@ -7,7 +7,7 @@ from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import DBusError, HostAppArmorError
|
||||
from ..utils.apparmor import validate_profile
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
SYSTEMD_SERVICES = {"hassos-apparmor.service", "hassio-apparmor.service"}
|
||||
|
||||
|
@ -4,7 +4,7 @@ import logging
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import HostNotSupportedError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
MANAGER = "manager"
|
||||
HOSTNAME = "hostname"
|
||||
|
@ -1,10 +1,11 @@
|
||||
"""Info control for host."""
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import HassioError, HostNotSupportedError
|
||||
from ..exceptions import HostNotSupportedError, DBusNotConnectedError, DBusError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class InfoCenter(CoreSysAttributes):
|
||||
@ -13,46 +14,44 @@ class InfoCenter(CoreSysAttributes):
|
||||
def __init__(self, coresys):
|
||||
"""Initialize system center handling."""
|
||||
self.coresys = coresys
|
||||
self._data = {}
|
||||
|
||||
@property
|
||||
def hostname(self):
|
||||
def hostname(self) -> Optional[str]:
|
||||
"""Return local hostname."""
|
||||
return self._data.get("StaticHostname") or None
|
||||
return self.sys_dbus.hostname.hostname
|
||||
|
||||
@property
|
||||
def chassis(self):
|
||||
def chassis(self) -> Optional[str]:
|
||||
"""Return local chassis type."""
|
||||
return self._data.get("Chassis") or None
|
||||
return self.sys_dbus.hostname.chassis
|
||||
|
||||
@property
|
||||
def deployment(self):
|
||||
def deployment(self) -> Optional[str]:
|
||||
"""Return local deployment type."""
|
||||
return self._data.get("Deployment") or None
|
||||
return self.sys_dbus.hostname.deployment
|
||||
|
||||
@property
|
||||
def kernel(self):
|
||||
def kernel(self) -> Optional[str]:
|
||||
"""Return local kernel version."""
|
||||
return self._data.get("KernelRelease") or None
|
||||
return self.sys_dbus.hostname.kernel
|
||||
|
||||
@property
|
||||
def operating_system(self):
|
||||
def operating_system(self) -> Optional[str]:
|
||||
"""Return local operating system."""
|
||||
return self._data.get("OperatingSystemPrettyName") or None
|
||||
return self.sys_dbus.hostname.operating_system
|
||||
|
||||
@property
|
||||
def cpe(self):
|
||||
def cpe(self) -> Optional[str]:
|
||||
"""Return local CPE."""
|
||||
return self._data.get("OperatingSystemCPEName") or None
|
||||
return self.sys_dbus.hostname.cpe
|
||||
|
||||
async def update(self):
|
||||
"""Update properties over dbus."""
|
||||
if not self.sys_dbus.hostname.is_connected:
|
||||
_LOGGER.error("No hostname D-Bus connection available")
|
||||
raise HostNotSupportedError()
|
||||
|
||||
_LOGGER.info("Update local host information")
|
||||
try:
|
||||
self._data = await self.sys_dbus.hostname.get_properties()
|
||||
except HassioError:
|
||||
await self.sys_dbus.hostname.update()
|
||||
except DBusError:
|
||||
_LOGGER.warning("Can't update host system information!")
|
||||
except DBusNotConnectedError:
|
||||
_LOGGER.error("No hostname D-Bus connection available")
|
||||
raise HostNotSupportedError() from None
|
||||
|
39
hassio/host/network.py
Normal file
39
hassio/host/network.py
Normal file
@ -0,0 +1,39 @@
|
||||
"""Info control for host."""
|
||||
import logging
|
||||
from typing import List, Set
|
||||
|
||||
from ..coresys import CoreSysAttributes, CoreSys
|
||||
from ..exceptions import HostNotSupportedError, DBusNotConnectedError, DBusError
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NetworkManager(CoreSysAttributes):
|
||||
"""Handle local network setup."""
|
||||
|
||||
def __init__(self, coresys: CoreSys):
|
||||
"""Initialize system center handling."""
|
||||
self.coresys: CoreSys = coresys
|
||||
|
||||
@property
|
||||
def dns_servers(self) -> List[str]:
|
||||
"""Return a list of local DNS servers."""
|
||||
# Read all local dns servers
|
||||
servers: Set[str] = set()
|
||||
for config in self.sys_dbus.nmi_dns.configuration:
|
||||
if config.vpn or not config.nameservers:
|
||||
continue
|
||||
servers |= set(config.nameservers)
|
||||
|
||||
return [f"dns://{server}" for server in servers]
|
||||
|
||||
async def update(self):
|
||||
"""Update properties over dbus."""
|
||||
_LOGGER.info("Update local network DNS information")
|
||||
try:
|
||||
await self.sys_dbus.nmi_dns.update()
|
||||
except DBusError:
|
||||
_LOGGER.warning("Can't update host DNS system information!")
|
||||
except DBusNotConnectedError:
|
||||
_LOGGER.error("No hostname D-Bus connection available")
|
||||
raise HostNotSupportedError() from None
|
@ -6,7 +6,7 @@ import attr
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..exceptions import HassioError, HostNotSupportedError, HostServiceError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
MOD_REPLACE = "replace"
|
||||
|
||||
|
@ -12,7 +12,7 @@ from .utils.dt import utc_from_timestamp, utcnow
|
||||
from .utils.json import JsonConfig
|
||||
from .validate import SCHEMA_INGRESS_CONFIG
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Ingress(JsonConfig, CoreSysAttributes):
|
||||
|
@ -7,7 +7,7 @@ from typing import Optional
|
||||
|
||||
import async_timeout
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
COMMAND = "socat UDP-RECVFROM:53,fork UDP-SENDTO:{!s}:53"
|
||||
|
||||
|
@ -9,7 +9,7 @@ import pyudev
|
||||
|
||||
from ..const import ATTR_DEVICES, ATTR_NAME, ATTR_TYPE, CHAN_ID, CHAN_TYPE
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
ASOUND_CARDS: Path = Path("/proc/asound/cards")
|
||||
RE_CARDS: re.Pattern = re.compile(r"(\d+) \[(\w*) *\]: (.*\w)")
|
||||
|
@ -3,7 +3,7 @@ import asyncio
|
||||
from datetime import date, datetime, time, timedelta
|
||||
import logging
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
INTERVAL = "interval"
|
||||
REPEAT = "repeat"
|
||||
|
@ -19,7 +19,7 @@ from ..const import (
|
||||
)
|
||||
from ..interface import ServiceInterface
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# pylint: disable=no-value-for-parameter
|
||||
|
@ -9,7 +9,7 @@ from ..const import FOLDER_HOMEASSISTANT, SNAPSHOT_FULL, SNAPSHOT_PARTIAL
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..utils.dt import utcnow
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SnapshotManager(CoreSysAttributes):
|
||||
|
@ -45,7 +45,7 @@ from ..utils.tar import SecureTarFile
|
||||
from .utils import key_to_iv, password_for_validating, password_to_key, remove_folder
|
||||
from .validate import ALL_FOLDERS, SCHEMA_SNAPSHOT
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Snapshot(CoreSysAttributes):
|
||||
|
@ -9,7 +9,7 @@ from .addon import AddonStore
|
||||
from .data import StoreData
|
||||
from .repository import Repository
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
BUILTIN_REPOSITORIES = set((REPOSITORY_CORE, REPOSITORY_LOCAL))
|
||||
|
||||
|
@ -4,7 +4,7 @@ import logging
|
||||
from ..coresys import CoreSys
|
||||
from ..addons.model import AddonModel, Data
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AddonStore(AddonModel):
|
||||
|
@ -20,7 +20,7 @@ from ..utils.json import read_json_file
|
||||
from .utils import extract_hash_from_path
|
||||
from .validate import SCHEMA_REPOSITORY_CONFIG
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StoreData(CoreSysAttributes):
|
||||
|
@ -12,7 +12,7 @@ from ..const import URL_HASSIO_ADDONS, ATTR_URL, ATTR_BRANCH
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..validate import RE_REPOSITORY
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GitRepo(CoreSysAttributes):
|
||||
|
@ -5,7 +5,7 @@ from pathlib import Path
|
||||
import re
|
||||
|
||||
RE_SHA1 = re.compile(r"[a-f0-9]{8}")
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_hash_from_repository(name: str) -> str:
|
||||
|
@ -20,7 +20,7 @@ from .exceptions import (
|
||||
SupervisorUpdateError,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Supervisor(CoreSysAttributes):
|
||||
|
@ -5,7 +5,7 @@ import logging
|
||||
from .coresys import CoreSysAttributes
|
||||
from .exceptions import HomeAssistantError, CoreDNSError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
HASS_WATCHDOG_API = "HASS_WATCHDOG_API"
|
||||
|
||||
|
@ -24,7 +24,7 @@ from .utils import AsyncThrottle
|
||||
from .utils.json import JsonConfig
|
||||
from .validate import SCHEMA_UPDATER_CONFIG
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Updater(JsonConfig, CoreSysAttributes):
|
||||
|
@ -5,7 +5,7 @@ import logging
|
||||
import re
|
||||
import socket
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
RE_STRING = re.compile(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))")
|
||||
|
||||
|
||||
|
@ -4,7 +4,7 @@ import re
|
||||
|
||||
from ..exceptions import AppArmorFileError, AppArmorInvalidError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
RE_PROFILE = re.compile(r"^profile ([^ ]+).*$")
|
||||
|
||||
|
@ -12,7 +12,7 @@ UTC = pytz.utc
|
||||
|
||||
GEOIP_URL = "http://ip-api.com/json/"
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Copyright (c) Django Software Foundation and individual contributors.
|
||||
|
@ -1,79 +1,96 @@
|
||||
"""DBus implementation with glib."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import json
|
||||
import shlex
|
||||
import re
|
||||
from signal import SIGINT
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from ..exceptions import DBusFatalError, DBusParseError
|
||||
from ..exceptions import (
|
||||
DBusFatalError,
|
||||
DBusParseError,
|
||||
DBusInterfaceError,
|
||||
DBusNotConnectedError,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
# Use to convert GVariant into json
|
||||
RE_GVARIANT_TYPE = re.compile(
|
||||
RE_GVARIANT_TYPE: re.Match = re.compile(
|
||||
r"(?:boolean|byte|int16|uint16|int32|uint32|handle|int64|uint64|double|"
|
||||
r"string|objectpath|signature) "
|
||||
)
|
||||
RE_GVARIANT_VARIANT = re.compile(
|
||||
r"(?<=(?: |{|\[))<((?:'|\").*?(?:'|\")|\d+(?:\.\d+)?)>(?=(?:|]|}|,))"
|
||||
RE_GVARIANT_VARIANT: re.Match = re.compile(r"\"[^\"\\]*(?:\\.[^\"\\]*)*\"|(<|>)")
|
||||
RE_GVARIANT_STRING_ESC: re.Match = re.compile(
|
||||
r"(?<=(?: |{|\[|\(|<))'[^']*?\"[^']*?'(?=(?:|]|}|,|\)|>))"
|
||||
)
|
||||
RE_GVARIANT_STRING = re.compile(r"(?<=(?: |{|\[|\())'(.*?)'(?=(?:|]|}|,|\)))")
|
||||
RE_GVARIANT_TUPLE_O = re.compile(r"\"[^\"]*?\"|(\()")
|
||||
RE_GVARIANT_TUPLE_C = re.compile(r"\"[^\"]*?\"|(,?\))")
|
||||
RE_GVARIANT_STRING: re.Match = re.compile(
|
||||
r"(?<=(?: |{|\[|\(|<))'(.*?)'(?=(?:|]|}|,|\)|>))"
|
||||
)
|
||||
RE_GVARIANT_TUPLE_O: re.Match = re.compile(r"\"[^\"\\]*(?:\\.[^\"\\]*)*\"|(\()")
|
||||
RE_GVARIANT_TUPLE_C: re.Match = re.compile(r"\"[^\"\\]*(?:\\.[^\"\\]*)*\"|(,?\))")
|
||||
|
||||
RE_MONITOR_OUTPUT = re.compile(r".+?: (?P<signal>[^ ].+) (?P<data>.*)")
|
||||
RE_MONITOR_OUTPUT: re.Match = re.compile(r".+?: (?P<signal>[^ ].+) (?P<data>.*)")
|
||||
|
||||
# Map GDBus to errors
|
||||
MAP_GDBUS_ERROR: Dict[str, Any] = {
|
||||
"GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown": DBusInterfaceError,
|
||||
"No such file or directory": DBusNotConnectedError,
|
||||
}
|
||||
|
||||
# Commands for dbus
|
||||
INTROSPECT = "gdbus introspect --system --dest {bus} " "--object-path {object} --xml"
|
||||
CALL = (
|
||||
INTROSPECT: str = "gdbus introspect --system --dest {bus} " "--object-path {object} --xml"
|
||||
CALL: str = (
|
||||
"gdbus call --system --dest {bus} --object-path {object} "
|
||||
"--method {method} {args}"
|
||||
)
|
||||
MONITOR = "gdbus monitor --system --dest {bus}"
|
||||
MONITOR: str = "gdbus monitor --system --dest {bus}"
|
||||
|
||||
DBUS_METHOD_GETALL = "org.freedesktop.DBus.Properties.GetAll"
|
||||
DBUS_METHOD_GETALL: str = "org.freedesktop.DBus.Properties.GetAll"
|
||||
|
||||
|
||||
class DBus:
|
||||
"""DBus handler."""
|
||||
|
||||
def __init__(self, bus_name, object_path):
|
||||
def __init__(self, bus_name: str, object_path: str) -> None:
|
||||
"""Initialize dbus object."""
|
||||
self.bus_name = bus_name
|
||||
self.object_path = object_path
|
||||
self.methods = set()
|
||||
self.signals = set()
|
||||
self.bus_name: str = bus_name
|
||||
self.object_path: str = object_path
|
||||
self.methods: Set[str] = set()
|
||||
self.signals: Set[str] = set()
|
||||
|
||||
@staticmethod
|
||||
async def connect(bus_name, object_path):
|
||||
async def connect(bus_name: str, object_path: str) -> DBus:
|
||||
"""Read object data."""
|
||||
self = DBus(bus_name, object_path)
|
||||
await self._init_proxy() # pylint: disable=protected-access
|
||||
|
||||
# pylint: disable=protected-access
|
||||
await self._init_proxy()
|
||||
|
||||
_LOGGER.info("Connect to dbus: %s - %s", bus_name, object_path)
|
||||
return self
|
||||
|
||||
async def _init_proxy(self):
|
||||
async def _init_proxy(self) -> None:
|
||||
"""Read interface data."""
|
||||
command = shlex.split(
|
||||
INTROSPECT.format(bus=self.bus_name, object=self.object_path)
|
||||
)
|
||||
|
||||
# Ask data
|
||||
_LOGGER.info("Introspect %s on %s", self.bus_name, self.object_path)
|
||||
data = await self._send(command)
|
||||
|
||||
# Parse XML
|
||||
data = await self._send(command)
|
||||
try:
|
||||
xml = ET.fromstring(data)
|
||||
except ET.ParseError as err:
|
||||
_LOGGER.error("Can't parse introspect data: %s", err)
|
||||
_LOGGER.debug("Introspect %s on %s", self.bus_name, self.object_path)
|
||||
raise DBusParseError() from None
|
||||
|
||||
# Read available methods
|
||||
_LOGGER.debug("data: %s", data)
|
||||
_LOGGER.debug("Introspect XML: %s", data)
|
||||
for interface in xml.findall("./interface"):
|
||||
interface_name = interface.get("name")
|
||||
|
||||
@ -88,30 +105,36 @@ class DBus:
|
||||
self.signals.add(f"{interface_name}.{signal_name}")
|
||||
|
||||
@staticmethod
|
||||
def parse_gvariant(raw):
|
||||
def parse_gvariant(raw: str) -> Any:
|
||||
"""Parse GVariant input to python."""
|
||||
raw = RE_GVARIANT_TYPE.sub("", raw)
|
||||
raw = RE_GVARIANT_VARIANT.sub(r"\1", raw)
|
||||
raw = RE_GVARIANT_STRING.sub(r'"\1"', raw)
|
||||
raw = RE_GVARIANT_TUPLE_O.sub(
|
||||
lambda x: x.group(0) if not x.group(1) else "[", raw
|
||||
json_raw: str = RE_GVARIANT_TYPE.sub("", raw)
|
||||
json_raw = RE_GVARIANT_STRING_ESC.sub(
|
||||
lambda x: x.group(0).replace('"', '\\"'), json_raw
|
||||
)
|
||||
raw = RE_GVARIANT_TUPLE_C.sub(
|
||||
lambda x: x.group(0) if not x.group(1) else "]", raw
|
||||
json_raw = RE_GVARIANT_STRING.sub(r'"\1"', json_raw)
|
||||
json_raw = RE_GVARIANT_VARIANT.sub(
|
||||
lambda x: x.group(0) if not x.group(1) else "", json_raw
|
||||
)
|
||||
json_raw = RE_GVARIANT_TUPLE_O.sub(
|
||||
lambda x: x.group(0) if not x.group(1) else "[", json_raw
|
||||
)
|
||||
json_raw = RE_GVARIANT_TUPLE_C.sub(
|
||||
lambda x: x.group(0) if not x.group(1) else "]", json_raw
|
||||
)
|
||||
|
||||
# No data
|
||||
if raw.startswith("[]"):
|
||||
if json_raw.startswith("[]"):
|
||||
return []
|
||||
|
||||
try:
|
||||
return json.loads(raw)
|
||||
return json.loads(json_raw)
|
||||
except json.JSONDecodeError as err:
|
||||
_LOGGER.error("Can't parse '%s': %s", raw, err)
|
||||
_LOGGER.error("Can't parse '%s': %s", json_raw, err)
|
||||
_LOGGER.debug("GVariant data: '%s'", raw)
|
||||
raise DBusParseError() from None
|
||||
|
||||
@staticmethod
|
||||
def gvariant_args(args):
|
||||
def gvariant_args(args: List[Any]) -> str:
|
||||
"""Convert args into gvariant."""
|
||||
gvariant = ""
|
||||
for arg in args:
|
||||
@ -122,11 +145,11 @@ class DBus:
|
||||
elif isinstance(arg, str):
|
||||
gvariant += f' "{arg}"'
|
||||
else:
|
||||
gvariant += " {}".format(str(arg))
|
||||
gvariant += f" {arg!s}"
|
||||
|
||||
return gvariant.lstrip()
|
||||
|
||||
async def call_dbus(self, method, *args):
|
||||
async def call_dbus(self, method: str, *args: List[Any]) -> str:
|
||||
"""Call a dbus method."""
|
||||
command = shlex.split(
|
||||
CALL.format(
|
||||
@ -142,10 +165,9 @@ class DBus:
|
||||
data = await self._send(command)
|
||||
|
||||
# Parse and return data
|
||||
_LOGGER.debug("Receive from %s: %s", method, data)
|
||||
return self.parse_gvariant(data)
|
||||
|
||||
async def get_properties(self, interface):
|
||||
async def get_properties(self, interface: str) -> Dict[str, Any]:
|
||||
"""Read all properties from interface."""
|
||||
try:
|
||||
return (await self.call_dbus(DBUS_METHOD_GETALL, interface))[0]
|
||||
@ -153,7 +175,7 @@ class DBus:
|
||||
_LOGGER.error("No attributes returned for %s", interface)
|
||||
raise DBusFatalError from None
|
||||
|
||||
async def _send(self, command):
|
||||
async def _send(self, command: List[str]) -> str:
|
||||
"""Send command over dbus."""
|
||||
# Run command
|
||||
_LOGGER.debug("Send dbus command: %s", command)
|
||||
@ -171,12 +193,19 @@ class DBus:
|
||||
raise DBusFatalError() from None
|
||||
|
||||
# Success?
|
||||
if proc.returncode != 0:
|
||||
_LOGGER.error("DBus return error: %s", error)
|
||||
raise DBusFatalError()
|
||||
if proc.returncode == 0:
|
||||
return data.decode()
|
||||
|
||||
# End
|
||||
return data.decode()
|
||||
# Filter error
|
||||
error = error.decode()
|
||||
for msg, exception in MAP_GDBUS_ERROR.items():
|
||||
if msg not in error:
|
||||
continue
|
||||
raise exception()
|
||||
|
||||
# General
|
||||
_LOGGER.error("DBus return error: %s", error)
|
||||
raise DBusFatalError()
|
||||
|
||||
def attach_signals(self, filters=None):
|
||||
"""Generate a signals wrapper."""
|
||||
@ -189,7 +218,7 @@ class DBus:
|
||||
async for signal in signals:
|
||||
return signal
|
||||
|
||||
def __getattr__(self, name):
|
||||
def __getattr__(self, name: str) -> DBusCallWrapper:
|
||||
"""Mapping to dbus method."""
|
||||
return getattr(DBusCallWrapper(self, self.bus_name), name)
|
||||
|
||||
@ -197,17 +226,17 @@ class DBus:
|
||||
class DBusCallWrapper:
|
||||
"""Wrapper a DBus interface for a call."""
|
||||
|
||||
def __init__(self, dbus, interface):
|
||||
def __init__(self, dbus: DBus, interface: str) -> None:
|
||||
"""Initialize wrapper."""
|
||||
self.dbus = dbus
|
||||
self.interface = interface
|
||||
self.dbus: DBus = dbus
|
||||
self.interface: str = interface
|
||||
|
||||
def __call__(self):
|
||||
def __call__(self) -> None:
|
||||
"""Should never be called."""
|
||||
_LOGGER.error("DBus method %s not exists!", self.interface)
|
||||
raise DBusFatalError()
|
||||
|
||||
def __getattr__(self, name):
|
||||
def __getattr__(self, name: str):
|
||||
"""Mapping to dbus method."""
|
||||
interface = f"{self.interface}.{name}"
|
||||
|
||||
@ -227,11 +256,11 @@ class DBusCallWrapper:
|
||||
class DBusSignalWrapper:
|
||||
"""Process Signals."""
|
||||
|
||||
def __init__(self, dbus, signals=None):
|
||||
def __init__(self, dbus: DBus, signals: Optional[str] = None):
|
||||
"""Initialize dbus signal wrapper."""
|
||||
self.dbus = dbus
|
||||
self._signals = signals
|
||||
self._proc = None
|
||||
self.dbus: DBus = dbus
|
||||
self._signals: Optional[str] = signals
|
||||
self._proc: Optional[asyncio.Process] = None
|
||||
|
||||
async def __aenter__(self):
|
||||
"""Start monitor events."""
|
||||
|
@ -9,7 +9,7 @@ from voluptuous.humanize import humanize_error
|
||||
|
||||
from ..exceptions import JsonFileError
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def write_json_file(jsonfile: Path, data: Any) -> None:
|
||||
|
@ -6,7 +6,7 @@ colorlog==4.0.2
|
||||
cpe==1.2.1
|
||||
cryptography==2.7
|
||||
docker==4.0.2
|
||||
gitpython==3.0.1
|
||||
gitpython==3.0.2
|
||||
pytz==2019.2
|
||||
pyudev==0.21.0
|
||||
uvloop==0.12.2
|
||||
|
@ -1,5 +1,5 @@
|
||||
flake8==3.7.8
|
||||
pylint==2.3.1
|
||||
pytest==5.1.0
|
||||
pytest==5.1.1
|
||||
pytest-timeout==1.3.3
|
||||
pytest-aiohttp==0.3.0
|
||||
|
302
tests/utils/test_gvariant_parser.py
Normal file
302
tests/utils/test_gvariant_parser.py
Normal file
@ -0,0 +1,302 @@
|
||||
"""Test gdbus gvariant parser."""
|
||||
from hassio.utils.gdbus import DBus
|
||||
|
||||
|
||||
def test_simple_return():
|
||||
"""Test Simple return value."""
|
||||
raw = "(objectpath '/org/freedesktop/systemd1/job/35383',)"
|
||||
|
||||
# parse data
|
||||
data = DBus.parse_gvariant(raw)
|
||||
|
||||
assert data == ["/org/freedesktop/systemd1/job/35383"]
|
||||
|
||||
|
||||
def test_get_property():
|
||||
"""Test Property parsing."""
|
||||
raw = "({'Hostname': <'hassio'>, 'StaticHostname': <'hassio'>, 'PrettyHostname': <''>, 'IconName': <'computer-embedded'>, 'Chassis': <'embedded'>, 'Deployment': <'production'>, 'Location': <''>, 'KernelName': <'Linux'>, 'KernelRelease': <'4.14.98-v7'>, 'KernelVersion': <'#1 SMP Sat May 11 02:17:06 UTC 2019'>, 'OperatingSystemPrettyName': <'HassOS 2.12'>, 'OperatingSystemCPEName': <'cpe:2.3:o:home_assistant:hassos:2.12:*:production:*:*:*:rpi3:*'>, 'HomeURL': <'https://hass.io/'>},)"
|
||||
|
||||
# parse data
|
||||
data = DBus.parse_gvariant(raw)
|
||||
|
||||
assert data[0] == {
|
||||
"Hostname": "hassio",
|
||||
"StaticHostname": "hassio",
|
||||
"PrettyHostname": "",
|
||||
"IconName": "computer-embedded",
|
||||
"Chassis": "embedded",
|
||||
"Deployment": "production",
|
||||
"Location": "",
|
||||
"KernelName": "Linux",
|
||||
"KernelRelease": "4.14.98-v7",
|
||||
"KernelVersion": "#1 SMP Sat May 11 02:17:06 UTC 2019",
|
||||
"OperatingSystemPrettyName": "HassOS 2.12",
|
||||
"OperatingSystemCPEName": "cpe:2.3:o:home_assistant:hassos:2.12:*:production:*:*:*:rpi3:*",
|
||||
"HomeURL": "https://hass.io/",
|
||||
}
|
||||
|
||||
|
||||
def test_systemd_unitlist_simple():
|
||||
"""Test Systemd Unit list simple."""
|
||||
raw = "([('systemd-remount-fs.service', 'Remount Root and Kernel File Systems', 'loaded', 'active', 'exited', '', objectpath '/org/freedesktop/systemd1/unit/systemd_2dremount_2dfs_2eservice', uint32 0, '', objectpath '/'), ('sys-subsystem-net-devices-veth5714b4e.device', '/sys/subsystem/net/devices/veth5714b4e', 'loaded', 'active', 'plugged', '', '/org/freedesktop/systemd1/unit/sys_2dsubsystem_2dnet_2ddevices_2dveth5714b4e_2edevice', 0, '', '/'), ('rauc.service', 'Rauc Update Service', 'loaded', 'active', 'running', '', '/org/freedesktop/systemd1/unit/rauc_2eservice', 0, '', '/'), ('mnt-data-docker-overlay2-7493c48dd99ab0e68420e3317d93711630dd55a76d4f2a21863a220031203ac2-merged.mount', '/mnt/data/docker/overlay2/7493c48dd99ab0e68420e3317d93711630dd55a76d4f2a21863a220031203ac2/merged', 'loaded', 'active', 'mounted', '', '/org/freedesktop/systemd1/unit/mnt_2ddata_2ddocker_2doverlay2_2d7493c48dd99ab0e68420e3317d93711630dd55a76d4f2a21863a220031203ac2_2dmerged_2emount', 0, '', '/'), ('hassos-hardware.target', 'HassOS hardware targets', 'loaded', 'active', 'active', '', '/org/freedesktop/systemd1/unit/hassos_2dhardware_2etarget', 0, '', '/'), ('dev-zram1.device', '/dev/zram1', 'loaded', 'active', 'plugged', 'sys-devices-virtual-block-zram1.device', '/org/freedesktop/systemd1/unit/dev_2dzram1_2edevice', 0, '', '/'), ('sys-subsystem-net-devices-hassio.device', '/sys/subsystem/net/devices/hassio', 'loaded', 'active', 'plugged', '', '/org/freedesktop/systemd1/unit/sys_2dsubsystem_2dnet_2ddevices_2dhassio_2edevice', 0, '', '/'), ('cryptsetup.target', 'cryptsetup.target', 'not-found', 'inactive', 'dead', '', '/org/freedesktop/systemd1/unit/cryptsetup_2etarget', 0, '', '/'), ('sys-devices-virtual-net-vethd256dfa.device', '/sys/devices/virtual/net/vethd256dfa', 'loaded', 'active', 'plugged', '', '/org/freedesktop/systemd1/unit/sys_2ddevices_2dvirtual_2dnet_2dvethd256dfa_2edevice', 0, '', '/'), ('network-pre.target', 'Network (Pre)', 'loaded', 'inactive', 'dead', '', '/org/freedesktop/systemd1/unit/network_2dpre_2etarget', 0, '', '/'), ('sys-devices-virtual-net-veth5714b4e.device', '/sys/devices/virtual/net/veth5714b4e', 'loaded', 'active', 'plugged', '', '/org/freedesktop/systemd1/unit/sys_2ddevices_2dvirtual_2dnet_2dveth5714b4e_2edevice', 0, '', '/'), ('sys-kernel-debug.mount', 'Kernel Debug File System', 'loaded', 'active', 'mounted', '', '/org/freedesktop/systemd1/unit/sys_2dkernel_2ddebug_2emount', 0, '', '/'), ('slices.target', 'Slices', 'loaded', 'active', 'active', '', '/org/freedesktop/systemd1/unit/slices_2etarget', 0, '', '/'), ('etc-NetworkManager-system\x2dconnections.mount', 'NetworkManager persistent system connections', 'loaded', 'active', 'mounted', '', '/org/freedesktop/systemd1/unit/etc_2dNetworkManager_2dsystem_5cx2dconnections_2emount', 0, '', '/'), ('run-docker-netns-26ede3178729.mount', '/run/docker/netns/26ede3178729', 'loaded', 'active', 'mounted', '', '/org/freedesktop/systemd1/unit/run_2ddocker_2dnetns_2d26ede3178729_2emount', 0, '', '/'), ('dev-disk-by\x2dpath-platform\x2d3f202000.mmc\x2dpart2.device', '/dev/disk/by-path/platform-3f202000.mmc-part2', 'loaded', 'active', 'plugged', 'sys-devices-platform-soc-3f202000.mmc-mmc_host-mmc0-mmc0:e624-block-mmcblk0-mmcblk0p2.device', '/org/freedesktop/systemd1/unit/dev_2ddisk_2dby_5cx2dpath_2dplatform_5cx2d3f202000_2emmc_5cx2dpart2_2edevice', 0, '', '/')],)"
|
||||
|
||||
# parse data
|
||||
data = DBus.parse_gvariant(raw)
|
||||
|
||||
assert data == [
|
||||
[
|
||||
[
|
||||
"systemd-remount-fs.service",
|
||||
"Remount Root and Kernel File Systems",
|
||||
"loaded",
|
||||
"active",
|
||||
"exited",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/systemd_2dremount_2dfs_2eservice",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"sys-subsystem-net-devices-veth5714b4e.device",
|
||||
"/sys/subsystem/net/devices/veth5714b4e",
|
||||
"loaded",
|
||||
"active",
|
||||
"plugged",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/sys_2dsubsystem_2dnet_2ddevices_2dveth5714b4e_2edevice",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"rauc.service",
|
||||
"Rauc Update Service",
|
||||
"loaded",
|
||||
"active",
|
||||
"running",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/rauc_2eservice",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"mnt-data-docker-overlay2-7493c48dd99ab0e68420e3317d93711630dd55a76d4f2a21863a220031203ac2-merged.mount",
|
||||
"/mnt/data/docker/overlay2/7493c48dd99ab0e68420e3317d93711630dd55a76d4f2a21863a220031203ac2/merged",
|
||||
"loaded",
|
||||
"active",
|
||||
"mounted",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/mnt_2ddata_2ddocker_2doverlay2_2d7493c48dd99ab0e68420e3317d93711630dd55a76d4f2a21863a220031203ac2_2dmerged_2emount",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"hassos-hardware.target",
|
||||
"HassOS hardware targets",
|
||||
"loaded",
|
||||
"active",
|
||||
"active",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/hassos_2dhardware_2etarget",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"dev-zram1.device",
|
||||
"/dev/zram1",
|
||||
"loaded",
|
||||
"active",
|
||||
"plugged",
|
||||
"sys-devices-virtual-block-zram1.device",
|
||||
"/org/freedesktop/systemd1/unit/dev_2dzram1_2edevice",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"sys-subsystem-net-devices-hassio.device",
|
||||
"/sys/subsystem/net/devices/hassio",
|
||||
"loaded",
|
||||
"active",
|
||||
"plugged",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/sys_2dsubsystem_2dnet_2ddevices_2dhassio_2edevice",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"cryptsetup.target",
|
||||
"cryptsetup.target",
|
||||
"not-found",
|
||||
"inactive",
|
||||
"dead",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/cryptsetup_2etarget",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"sys-devices-virtual-net-vethd256dfa.device",
|
||||
"/sys/devices/virtual/net/vethd256dfa",
|
||||
"loaded",
|
||||
"active",
|
||||
"plugged",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/sys_2ddevices_2dvirtual_2dnet_2dvethd256dfa_2edevice",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"network-pre.target",
|
||||
"Network (Pre)",
|
||||
"loaded",
|
||||
"inactive",
|
||||
"dead",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/network_2dpre_2etarget",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"sys-devices-virtual-net-veth5714b4e.device",
|
||||
"/sys/devices/virtual/net/veth5714b4e",
|
||||
"loaded",
|
||||
"active",
|
||||
"plugged",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/sys_2ddevices_2dvirtual_2dnet_2dveth5714b4e_2edevice",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"sys-kernel-debug.mount",
|
||||
"Kernel Debug File System",
|
||||
"loaded",
|
||||
"active",
|
||||
"mounted",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/sys_2dkernel_2ddebug_2emount",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"slices.target",
|
||||
"Slices",
|
||||
"loaded",
|
||||
"active",
|
||||
"active",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/slices_2etarget",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"etc-NetworkManager-system-connections.mount",
|
||||
"NetworkManager persistent system connections",
|
||||
"loaded",
|
||||
"active",
|
||||
"mounted",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/etc_2dNetworkManager_2dsystem_5cx2dconnections_2emount",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"run-docker-netns-26ede3178729.mount",
|
||||
"/run/docker/netns/26ede3178729",
|
||||
"loaded",
|
||||
"active",
|
||||
"mounted",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/run_2ddocker_2dnetns_2d26ede3178729_2emount",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"dev-disk-by-path-platform-3f202000.mmc-part2.device",
|
||||
"/dev/disk/by-path/platform-3f202000.mmc-part2",
|
||||
"loaded",
|
||||
"active",
|
||||
"plugged",
|
||||
"sys-devices-platform-soc-3f202000.mmc-mmc_host-mmc0-mmc0:e624-block-mmcblk0-mmcblk0p2.device",
|
||||
"/org/freedesktop/systemd1/unit/dev_2ddisk_2dby_5cx2dpath_2dplatform_5cx2d3f202000_2emmc_5cx2dpart2_2edevice",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
def test_systemd_unitlist_complex():
|
||||
"""Test Systemd Unit list simple."""
|
||||
raw = "([('systemd-remount-fs.service', 'Remount Root and \"Kernel File Systems\"', 'loaded', 'active', 'exited', '', objectpath '/org/freedesktop/systemd1/unit/systemd_2dremount_2dfs_2eservice', uint32 0, '', objectpath '/'), ('sys-subsystem-net-devices-veth5714b4e.device', '/sys/subsystem/net/devices/veth5714b4e for \" is', 'loaded', 'active', 'plugged', '', '/org/freedesktop/systemd1/unit/sys_2dsubsystem_2dnet_2ddevices_2dveth5714b4e_2edevice', 0, '', '/')],)"
|
||||
|
||||
# parse data
|
||||
data = DBus.parse_gvariant(raw)
|
||||
|
||||
assert data == [
|
||||
[
|
||||
[
|
||||
"systemd-remount-fs.service",
|
||||
'Remount Root and "Kernel File Systems"',
|
||||
"loaded",
|
||||
"active",
|
||||
"exited",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/systemd_2dremount_2dfs_2eservice",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
[
|
||||
"sys-subsystem-net-devices-veth5714b4e.device",
|
||||
'/sys/subsystem/net/devices/veth5714b4e for " is',
|
||||
"loaded",
|
||||
"active",
|
||||
"plugged",
|
||||
"",
|
||||
"/org/freedesktop/systemd1/unit/sys_2dsubsystem_2dnet_2ddevices_2dveth5714b4e_2edevice",
|
||||
0,
|
||||
"",
|
||||
"/",
|
||||
],
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
def test_networkmanager_dns_properties():
|
||||
"""Test NetworkManager DNS properties."""
|
||||
raw = "({'Mode': <'default'>, 'RcManager': <'file'>, 'Configuration': <[{'nameservers': <['192.168.23.30']>, 'domains': <['syshack.local']>, 'interface': <'eth0'>, 'priority': <100>, 'vpn': <false>}]>},)"
|
||||
|
||||
# parse data
|
||||
data = DBus.parse_gvariant(raw)
|
||||
|
||||
assert data == [
|
||||
{
|
||||
"Mode": "default",
|
||||
"RcManager": "file",
|
||||
"Configuration": [
|
||||
{
|
||||
"nameservers": ["192.168.23.30"],
|
||||
"domains": ["syshack.local"],
|
||||
"interface": "eth0",
|
||||
"priority": 100,
|
||||
"vpn": False,
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
Loading…
x
Reference in New Issue
Block a user