mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-28 11:36:32 +00:00
commit
979861b764
@ -359,11 +359,17 @@ class AddonManager(CoreSysAttributes):
|
|||||||
"""Sync add-ons DNS names."""
|
"""Sync add-ons DNS names."""
|
||||||
# Update hosts
|
# Update hosts
|
||||||
for addon in self.installed:
|
for addon in self.installed:
|
||||||
if not await addon.instance.is_running():
|
try:
|
||||||
continue
|
if not await addon.instance.is_running():
|
||||||
self.sys_plugins.dns.add_host(
|
continue
|
||||||
ipv4=addon.ip_address, names=[addon.hostname], write=False
|
except DockerAPIError as err:
|
||||||
)
|
_LOGGER.warning("Add-on %s is corrupt: %s", addon.slug, err)
|
||||||
|
self.sys_core.healthy = False
|
||||||
|
self.sys_capture_exception(err)
|
||||||
|
else:
|
||||||
|
self.sys_plugins.dns.add_host(
|
||||||
|
ipv4=addon.ip_address, names=[addon.hostname], write=False
|
||||||
|
)
|
||||||
|
|
||||||
# Write hosts files
|
# Write hosts files
|
||||||
with suppress(CoreDNSError):
|
with suppress(CoreDNSError):
|
||||||
|
@ -558,7 +558,7 @@ class Addon(AddonModel):
|
|||||||
await self.instance.run()
|
await self.instance.run()
|
||||||
except DockerAPIError as err:
|
except DockerAPIError as err:
|
||||||
self.state = AddonState.ERROR
|
self.state = AddonState.ERROR
|
||||||
raise AddonsError(err) from None
|
raise AddonsError(err) from err
|
||||||
else:
|
else:
|
||||||
self.state = AddonState.STARTED
|
self.state = AddonState.STARTED
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ from enum import Enum
|
|||||||
from ipaddress import ip_network
|
from ipaddress import ip_network
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
SUPERVISOR_VERSION = "239"
|
SUPERVISOR_VERSION = "240"
|
||||||
|
|
||||||
URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons"
|
URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons"
|
||||||
URL_HASSIO_APPARMOR = "https://version.home-assistant.io/apparmor.txt"
|
URL_HASSIO_APPARMOR = "https://version.home-assistant.io/apparmor.txt"
|
||||||
@ -240,6 +240,7 @@ ATTR_SNAPSHOT_EXCLUDE = "snapshot_exclude"
|
|||||||
ATTR_SNAPSHOTS = "snapshots"
|
ATTR_SNAPSHOTS = "snapshots"
|
||||||
ATTR_SOURCE = "source"
|
ATTR_SOURCE = "source"
|
||||||
ATTR_SQUASH = "squash"
|
ATTR_SQUASH = "squash"
|
||||||
|
ATTR_SSID = "ssid"
|
||||||
ATTR_SSL = "ssl"
|
ATTR_SSL = "ssl"
|
||||||
ATTR_STAGE = "stage"
|
ATTR_STAGE = "stage"
|
||||||
ATTR_STARTUP = "startup"
|
ATTR_STARTUP = "startup"
|
||||||
|
@ -19,6 +19,8 @@ DBUS_OBJECT_HOSTNAME = "/org/freedesktop/hostname1"
|
|||||||
DBUS_OBJECT_NM = "/org/freedesktop/NetworkManager"
|
DBUS_OBJECT_NM = "/org/freedesktop/NetworkManager"
|
||||||
DBUS_OBJECT_SYSTEMD = "/org/freedesktop/systemd1"
|
DBUS_OBJECT_SYSTEMD = "/org/freedesktop/systemd1"
|
||||||
|
|
||||||
|
DBUS_ATTR_802_WIRELESS = "802-11-wireless"
|
||||||
|
DBUS_ATTR_802_WIRELESS_SECURITY = "802-11-wireless-security"
|
||||||
DBUS_ATTR_ACTIVE_CONNECTIONS = "ActiveConnections"
|
DBUS_ATTR_ACTIVE_CONNECTIONS = "ActiveConnections"
|
||||||
DBUS_ATTR_ADDRESS_DATA = "AddressData"
|
DBUS_ATTR_ADDRESS_DATA = "AddressData"
|
||||||
DBUS_ATTR_BOOT_SLOT = "BootSlot"
|
DBUS_ATTR_BOOT_SLOT = "BootSlot"
|
||||||
|
@ -60,3 +60,11 @@ class NetworkDevice:
|
|||||||
ip4_address: int = attr.ib()
|
ip4_address: int = attr.ib()
|
||||||
device_type: int = attr.ib()
|
device_type: int = attr.ib()
|
||||||
real: bool = attr.ib()
|
real: bool = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
|
class WirelessProperties:
|
||||||
|
"""WirelessProperties object for Network Manager."""
|
||||||
|
|
||||||
|
properties: dict = attr.ib()
|
||||||
|
security: dict = attr.ib()
|
||||||
|
@ -4,6 +4,8 @@ from typing import Optional
|
|||||||
from ...const import ATTR_ADDRESS, ATTR_IPV4, ATTR_METHOD, ATTR_PREFIX
|
from ...const import ATTR_ADDRESS, ATTR_IPV4, ATTR_METHOD, ATTR_PREFIX
|
||||||
from ...utils.gdbus import DBus
|
from ...utils.gdbus import DBus
|
||||||
from ..const import (
|
from ..const import (
|
||||||
|
DBUS_ATTR_802_WIRELESS,
|
||||||
|
DBUS_ATTR_802_WIRELESS_SECURITY,
|
||||||
DBUS_ATTR_ADDRESS_DATA,
|
DBUS_ATTR_ADDRESS_DATA,
|
||||||
DBUS_ATTR_CONNECTION,
|
DBUS_ATTR_CONNECTION,
|
||||||
DBUS_ATTR_DEFAULT,
|
DBUS_ATTR_DEFAULT,
|
||||||
@ -23,6 +25,7 @@ from ..const import (
|
|||||||
DBUS_NAME_IP4CONFIG,
|
DBUS_NAME_IP4CONFIG,
|
||||||
DBUS_NAME_NM,
|
DBUS_NAME_NM,
|
||||||
DBUS_OBJECT_BASE,
|
DBUS_OBJECT_BASE,
|
||||||
|
ConnectionType,
|
||||||
)
|
)
|
||||||
from .configuration import (
|
from .configuration import (
|
||||||
AddressData,
|
AddressData,
|
||||||
@ -30,6 +33,7 @@ from .configuration import (
|
|||||||
NetworkAttributes,
|
NetworkAttributes,
|
||||||
NetworkDevice,
|
NetworkDevice,
|
||||||
NetworkSettings,
|
NetworkSettings,
|
||||||
|
WirelessProperties,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -44,6 +48,7 @@ class NetworkConnection(NetworkAttributes):
|
|||||||
self._settings: Optional[NetworkSettings] = None
|
self._settings: Optional[NetworkSettings] = None
|
||||||
self._ip4_config: Optional[IpConfiguration] = None
|
self._ip4_config: Optional[IpConfiguration] = None
|
||||||
self._device: Optional[NetworkDevice]
|
self._device: Optional[NetworkDevice]
|
||||||
|
self._wireless: Optional[WirelessProperties] = None
|
||||||
self.primary: bool = False
|
self.primary: bool = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -81,6 +86,13 @@ class NetworkConnection(NetworkAttributes):
|
|||||||
"""Return the uuid of the connection."""
|
"""Return the uuid of the connection."""
|
||||||
return self._properties[DBUS_ATTR_UUID]
|
return self._properties[DBUS_ATTR_UUID]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def wireless(self) -> str:
|
||||||
|
"""Return wireless properties if any."""
|
||||||
|
if self.type != ConnectionType.WIRELESS:
|
||||||
|
return None
|
||||||
|
return self._wireless
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def state(self) -> int:
|
def state(self) -> int:
|
||||||
"""
|
"""
|
||||||
@ -119,6 +131,11 @@ class NetworkConnection(NetworkAttributes):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self._wireless = WirelessProperties(
|
||||||
|
data.get(DBUS_ATTR_802_WIRELESS, {}),
|
||||||
|
data.get(DBUS_ATTR_802_WIRELESS_SECURITY, {}),
|
||||||
|
)
|
||||||
|
|
||||||
self._device = NetworkDevice(
|
self._device = NetworkDevice(
|
||||||
device,
|
device,
|
||||||
device_data.get(DBUS_ATTR_DEVICE_INTERFACE),
|
device_data.get(DBUS_ATTR_DEVICE_INTERFACE),
|
||||||
|
@ -4,6 +4,7 @@ from ..const import (
|
|||||||
DBUS_NAME_CONNECTION_ACTIVE,
|
DBUS_NAME_CONNECTION_ACTIVE,
|
||||||
DBUS_NAME_NM,
|
DBUS_NAME_NM,
|
||||||
DBUS_OBJECT_BASE,
|
DBUS_OBJECT_BASE,
|
||||||
|
ConnectionType,
|
||||||
InterfaceMethod,
|
InterfaceMethod,
|
||||||
)
|
)
|
||||||
from ..payloads.generate import interface_update_payload
|
from ..payloads.generate import interface_update_payload
|
||||||
@ -49,7 +50,7 @@ class NetworkInterface:
|
|||||||
return self.connection.ip4_config.address_data.prefix
|
return self.connection.ip4_config.address_data.prefix
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self) -> str:
|
def type(self) -> ConnectionType:
|
||||||
"""Return the interface type."""
|
"""Return the interface type."""
|
||||||
return self.connection.type
|
return self.connection.type
|
||||||
|
|
||||||
|
@ -3,8 +3,8 @@ from pathlib import Path
|
|||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
|
|
||||||
from ...const import ATTR_ADDRESS, ATTR_DNS, ATTR_METHOD, ATTR_PREFIX
|
from ...const import ATTR_ADDRESS, ATTR_DNS, ATTR_METHOD, ATTR_PREFIX, ATTR_SSID
|
||||||
from ..const import InterfaceMethod
|
from ..const import ConnectionType, InterfaceMethod
|
||||||
from ..network.utils import ip2int
|
from ..network.utils import ip2int
|
||||||
|
|
||||||
INTERFACE_UPDATE_TEMPLATE: Path = (
|
INTERFACE_UPDATE_TEMPLATE: Path = (
|
||||||
@ -31,4 +31,15 @@ def interface_update_payload(interface, **kwargs) -> str:
|
|||||||
kwargs[ATTR_ADDRESS] = kwargs[ATTR_ADDRESS].split("/")[0]
|
kwargs[ATTR_ADDRESS] = kwargs[ATTR_ADDRESS].split("/")[0]
|
||||||
kwargs[ATTR_METHOD] = InterfaceMethod.MANUAL
|
kwargs[ATTR_METHOD] = InterfaceMethod.MANUAL
|
||||||
|
|
||||||
|
if interface.type == ConnectionType.WIRELESS:
|
||||||
|
kwargs[ATTR_SSID] = ", ".join(
|
||||||
|
[
|
||||||
|
f"0x{x}"
|
||||||
|
for x in interface.connection.wireless.properties[ATTR_SSID]
|
||||||
|
.encode()
|
||||||
|
.hex(",")
|
||||||
|
.split(",")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
return template.render(interface=interface, options=kwargs)
|
return template.render(interface=interface, options=kwargs)
|
||||||
|
@ -23,4 +23,18 @@
|
|||||||
'gateway': <'{{ options.get("gateway", interface.gateway) }}'>
|
'gateway': <'{{ options.get("gateway", interface.gateway) }}'>
|
||||||
}
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if interface.type == "802-11-wireless" %}
|
||||||
|
,
|
||||||
|
'802-11-wireless':
|
||||||
|
{
|
||||||
|
'security': <'802-11-wireless-security'>,
|
||||||
|
'ssid': <[byte {{ options.ssid }}]>
|
||||||
|
|
||||||
|
},
|
||||||
|
'802-11-wireless-security':
|
||||||
|
{
|
||||||
|
'auth-alg': <'{{ interface.connection.wireless.security['auth-alg'] }}'>,
|
||||||
|
'key-mgmt': <'{{ interface.connection.wireless.security['key-mgmt'] }}'>
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
}
|
}
|
@ -149,7 +149,7 @@ class DockerAPI:
|
|||||||
container.start()
|
container.start()
|
||||||
except (docker.errors.DockerException, requests.RequestException) as err:
|
except (docker.errors.DockerException, requests.RequestException) as err:
|
||||||
_LOGGER.error("Can't start %s: %s", name, err)
|
_LOGGER.error("Can't start %s: %s", name, err)
|
||||||
raise DockerAPIError(err) from None
|
raise DockerAPIError(err) from err
|
||||||
|
|
||||||
# Update metadata
|
# Update metadata
|
||||||
with suppress(docker.errors.DockerException, requests.RequestException):
|
with suppress(docker.errors.DockerException, requests.RequestException):
|
||||||
|
@ -308,7 +308,7 @@ class Tasks(CoreSysAttributes):
|
|||||||
try:
|
try:
|
||||||
await addon.start()
|
await addon.start()
|
||||||
except AddonsError as err:
|
except AddonsError as err:
|
||||||
_LOGGER.error("Watchdog %s reanimation failed!", addon.slug)
|
_LOGGER.error("Watchdog %s reanimation failed with %s", addon.slug, err)
|
||||||
self.sys_capture_exception(err)
|
self.sys_capture_exception(err)
|
||||||
|
|
||||||
async def _watchdog_addon_application(self):
|
async def _watchdog_addon_application(self):
|
||||||
@ -338,7 +338,7 @@ class Tasks(CoreSysAttributes):
|
|||||||
try:
|
try:
|
||||||
await addon.restart()
|
await addon.restart()
|
||||||
except AddonsError as err:
|
except AddonsError as err:
|
||||||
_LOGGER.error("Watchdog %s reanimation failed!", addon.slug)
|
_LOGGER.error("Watchdog %s reanimation failed with %s", addon.slug, err)
|
||||||
self.sys_capture_exception(err)
|
self.sys_capture_exception(err)
|
||||||
finally:
|
finally:
|
||||||
self._cache[addon.slug] = 0
|
self._cache[addon.slug] = 0
|
||||||
|
@ -74,7 +74,21 @@ class StoreData(CoreSysAttributes):
|
|||||||
|
|
||||||
def _read_addons_folder(self, path, repository):
|
def _read_addons_folder(self, path, repository):
|
||||||
"""Read data from add-ons folder."""
|
"""Read data from add-ons folder."""
|
||||||
for addon in path.glob("**/config.json"):
|
try:
|
||||||
|
addon_list = path.glob("**/config.json")
|
||||||
|
except OSError as err:
|
||||||
|
self.sys_core.healthy = False
|
||||||
|
_LOGGER.critical(
|
||||||
|
"Can't process %s because of Filesystem issues: %s", repository, err
|
||||||
|
)
|
||||||
|
self.sys_capture_exception(err)
|
||||||
|
return
|
||||||
|
|
||||||
|
for addon in addon_list:
|
||||||
|
# Ingore git artefacts
|
||||||
|
if ".git" in addon.parts:
|
||||||
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
addon_config = read_json_file(addon)
|
addon_config = read_json_file(addon)
|
||||||
except JsonFileError:
|
except JsonFileError:
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
"""Test interface update payload."""
|
"""Test interface update payload."""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from supervisor.dbus.const import ConnectionType
|
||||||
from supervisor.dbus.payloads.generate import interface_update_payload
|
from supervisor.dbus.payloads.generate import interface_update_payload
|
||||||
from supervisor.utils.gdbus import DBus
|
from supervisor.utils.gdbus import DBus
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_interface_update_payload(network_interface):
|
async def test_interface_update_payload_ethernet(network_interface):
|
||||||
"""Test interface update payload."""
|
"""Test interface update payload."""
|
||||||
data = interface_update_payload(network_interface, **{"method": "auto"})
|
data = interface_update_payload(network_interface, **{"method": "auto"})
|
||||||
assert DBus.parse_gvariant(data)["ipv4"]["method"] == "auto"
|
assert DBus.parse_gvariant(data)["ipv4"]["method"] == "auto"
|
||||||
@ -17,3 +18,18 @@ async def test_interface_update_payload(network_interface):
|
|||||||
assert DBus.parse_gvariant(data)["ipv4"]["method"] == "manual"
|
assert DBus.parse_gvariant(data)["ipv4"]["method"] == "manual"
|
||||||
assert DBus.parse_gvariant(data)["ipv4"]["address-data"][0]["address"] == "1.1.1.1"
|
assert DBus.parse_gvariant(data)["ipv4"]["address-data"][0]["address"] == "1.1.1.1"
|
||||||
assert DBus.parse_gvariant(data)["ipv4"]["dns"] == [16843009, 16777217]
|
assert DBus.parse_gvariant(data)["ipv4"]["dns"] == [16843009, 16777217]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_interface_update_payload_wireless(network_interface):
|
||||||
|
"""Test interface update payload."""
|
||||||
|
network_interface.connection._properties["Type"] = ConnectionType.WIRELESS
|
||||||
|
data = interface_update_payload(network_interface, **{"method": "auto"})
|
||||||
|
assert DBus.parse_gvariant(data)["ipv4"]["method"] == "auto"
|
||||||
|
|
||||||
|
data = interface_update_payload(
|
||||||
|
network_interface, **{"address": "1.1.1.1", "dns": ["1.1.1.1", "1.0.0.1"]}
|
||||||
|
)
|
||||||
|
assert DBus.parse_gvariant(data)["ipv4"]["method"] == "manual"
|
||||||
|
assert DBus.parse_gvariant(data)["ipv4"]["address-data"][0]["address"] == "1.1.1.1"
|
||||||
|
assert DBus.parse_gvariant(data)["802-11-wireless"]["ssid"] == "NETT"
|
||||||
|
@ -1 +1 @@
|
|||||||
({'connection': {'id': <'Wired connection 1'>, 'permissions': <@as []>, 'timestamp': <uint64 1598125548>, 'type': <'802-3-ethernet'>, 'uuid': <'0c23631e-2118-355c-bbb0-8943229cb0d6'>}, 'ipv4': {'address-data': <[{'address': <'192.168.2.148'>, 'prefix': <uint32 24>}]>, 'addresses': <[[uint32 2483202240, 24, 16951488]]>, 'dns': <[uint32 16951488]>, 'dns-search': <@as []>, 'gateway': <'192.168.2.1'>, 'method': <'auto'>, 'route-data': <@aa{sv} []>, 'routes': <@aau []>}, 'ipv6': {'address-data': <@aa{sv} []>, 'addresses': <@a(ayuay) []>, 'dns': <@aay []>, 'dns-search': <@as []>, 'method': <'auto'>, 'route-data': <@aa{sv} []>, 'routes': <@a(ayuayu) []>}, 'proxy': {}, '802-3-ethernet': {'auto-negotiate': <false>, 'mac-address-blacklist': <@as []>, 's390-options': <@a{ss} {}>}},)
|
({'connection': {'id': <'Wired connection 1'>, 'permissions': <@as []>, 'timestamp': <uint64 1598125548>, 'type': <'802-3-ethernet'>, 'uuid': <'0c23631e-2118-355c-bbb0-8943229cb0d6'>}, 'ipv4': {'address-data': <[{'address': <'192.168.2.148'>, 'prefix': <uint32 24>}]>, 'addresses': <[[uint32 2483202240, 24, 16951488]]>, 'dns': <[uint32 16951488]>, 'dns-search': <@as []>, 'gateway': <'192.168.2.1'>, 'method': <'auto'>, 'route-data': <@aa{sv} []>, 'routes': <@aau []>}, 'ipv6': {'address-data': <@aa{sv} []>, 'addresses': <@a(ayuay) []>, 'dns': <@aay []>, 'dns-search': <@as []>, 'method': <'auto'>, 'route-data': <@aa{sv} []>, 'routes': <@a(ayuayu) []>}, 'proxy': {}, '802-3-ethernet': {'auto-negotiate': <false>, 'mac-address-blacklist': <@as []>, 's390-options': <@a{ss} {}>}, '802-11-wireless': {'ssid': <[byte 0x4e, 0x45, 0x54, 0x54]>}},)
|
@ -1 +1 @@
|
|||||||
({'connection': {'id': <'Wired connection 1'>, 'permissions': <@as []>, 'timestamp': <uint64 1598125548>, 'type': <'802-3-ethernet'>, 'uuid': <'0c23631e-2118-355c-bbb0-8943229cb0d6'>}, 'ipv4': {'address-data': <[{'address': <'192.168.2.148'>, 'prefix': <uint32 24>}]>, 'addresses': <[[uint32 2483202240, 24, 16951488]]>, 'dns': <[uint32 16951488]>, 'dns-search': <@as []>, 'gateway': <'192.168.2.1'>, 'method': <'auto'>, 'route-data': <@aa{sv} []>, 'routes': <@aau []>}, 'ipv6': {'address-data': <@aa{sv} []>, 'addresses': <@a(ayuay) []>, 'dns': <@aay []>, 'dns-search': <@as []>, 'method': <'auto'>, 'route-data': <@aa{sv} []>, 'routes': <@a(ayuayu) []>}, 'proxy': {}, '802-3-ethernet': {'auto-negotiate': <false>, 'mac-address-blacklist': <@as []>, 's390-options': <@a{ss} {}>}},)
|
({'connection': {'id': <'Wired connection 1'>, 'permissions': <@as []>, 'timestamp': <uint64 1598125548>, 'type': <'802-3-ethernet'>, 'uuid': <'0c23631e-2118-355c-bbb0-8943229cb0d6'>}, 'ipv4': {'address-data': <[{'address': <'192.168.2.148'>, 'prefix': <uint32 24>}]>, 'addresses': <[[uint32 2483202240, 24, 16951488]]>, 'dns': <[uint32 16951488]>, 'dns-search': <@as []>, 'gateway': <'192.168.2.1'>, 'method': <'auto'>, 'route-data': <@aa{sv} []>, 'routes': <@aau []>}, 'ipv6': {'address-data': <@aa{sv} []>, 'addresses': <@a(ayuay) []>, 'dns': <@aay []>, 'dns-search': <@as []>, 'method': <'auto'>, 'route-data': <@aa{sv} []>, 'routes': <@a(ayuayu) []>}, 'proxy': {}, '802-3-ethernet': {'auto-negotiate': <false>, 'mac-address-blacklist': <@as []>, 's390-options': <@a{ss} {}>}, '802-11-wireless': {'ssid': <[byte 0x4e, 0x45, 0x54, 0x54]>}},)
|
Loading…
x
Reference in New Issue
Block a user