diff --git a/API.md b/API.md index eb8bd0bac..b1eb768b5 100644 --- a/API.md +++ b/API.md @@ -252,6 +252,10 @@ return: } ``` +- GET `/host/logs` + +Return host dmesg + - POST `/host/options` ```json diff --git a/supervisor/api/__init__.py b/supervisor/api/__init__.py index 846f2ff2f..e405f88e6 100644 --- a/supervisor/api/__init__.py +++ b/supervisor/api/__init__.py @@ -74,6 +74,7 @@ class RestAPI(CoreSysAttributes): self.webapp.add_routes( [ web.get("/host/info", api_host.info), + web.get("/host/logs", api_host.logs), web.post("/host/reboot", api_host.reboot), web.post("/host/shutdown", api_host.shutdown), web.post("/host/reload", api_host.reload), diff --git a/supervisor/api/host.py b/supervisor/api/host.py index d686863c5..2e2361164 100644 --- a/supervisor/api/host.py +++ b/supervisor/api/host.py @@ -1,24 +1,27 @@ """Init file for Supervisor host RESTful API.""" import asyncio import logging +from typing import Awaitable +from aiohttp import web import voluptuous as vol -from .utils import api_process, api_validate from ..const import ( - ATTR_HOSTNAME, - ATTR_FEATURES, - ATTR_KERNEL, - ATTR_OPERATING_SYSTEM, ATTR_CHASSIS, - ATTR_DEPLOYMENT, - ATTR_STATE, - ATTR_NAME, - ATTR_DESCRIPTON, - ATTR_SERVICES, ATTR_CPE, + ATTR_DEPLOYMENT, + ATTR_DESCRIPTON, + ATTR_FEATURES, + ATTR_HOSTNAME, + ATTR_KERNEL, + ATTR_NAME, + ATTR_OPERATING_SYSTEM, + ATTR_SERVICES, + ATTR_STATE, + CONTENT_TYPE_BINARY, ) from ..coresys import CoreSysAttributes +from .utils import api_process, api_process_raw, api_validate _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -107,3 +110,8 @@ class APIHost(CoreSysAttributes): """Restart a service.""" unit = request.match_info.get(SERVICE) return asyncio.shield(self.sys_host.services.restart(unit)) + + @api_process_raw(CONTENT_TYPE_BINARY) + def logs(self, request: web.Request) -> Awaitable[bytes]: + """Return host kernel logs.""" + return self.sys_host.info.get_dmesg() diff --git a/supervisor/cli.py b/supervisor/cli.py index 6b2d9bc49..4872d183a 100644 --- a/supervisor/cli.py +++ b/supervisor/cli.py @@ -65,7 +65,7 @@ class HaCli(CoreSysAttributes, JsonConfig): await self.instance.attach(tag=self.version) except DockerAPIError: - _LOGGER.info("No Audio plugin Docker image %s found.", self.instance.image) + _LOGGER.info("No cli plugin Docker image %s found.", self.instance.image) # Install cli with suppress(CliError): @@ -89,7 +89,7 @@ class HaCli(CoreSysAttributes, JsonConfig): if self.latest_version: with suppress(DockerAPIError): - await self.instance.install(self.latest_version) + await self.instance.install(self.latest_version, latest=True) break _LOGGER.warning("Error on install cli plugin. Retry in 30sec") await asyncio.sleep(30) @@ -111,10 +111,16 @@ class HaCli(CoreSysAttributes, JsonConfig): except DockerAPIError: _LOGGER.error("HA cli update fails") raise CliUpdateError() from None - else: - # Cleanup - with suppress(DockerAPIError): - await self.instance.cleanup() + + # Cleanup + with suppress(DockerAPIError): + await self.instance.cleanup() + + self.version = version + self.save_data() + + # Start cli + await self.start() async def start(self) -> None: """Run cli.""" diff --git a/supervisor/const.py b/supervisor/const.py index a666033a0..9746c4470 100644 --- a/supervisor/const.py +++ b/supervisor/const.py @@ -3,7 +3,7 @@ from enum import Enum from ipaddress import ip_network from pathlib import Path -SUPERVISOR_VERSION = "211" +SUPERVISOR_VERSION = "212" URL_HASSIO_ADDONS = "https://github.com/home-assistant/hassio-addons" diff --git a/supervisor/dns.py b/supervisor/dns.py index 98b20cf57..e25c35cc8 100644 --- a/supervisor/dns.py +++ b/supervisor/dns.py @@ -165,15 +165,16 @@ class CoreDNS(JsonConfig, CoreSysAttributes): _LOGGER.warning("Version %s is already installed for CoreDNS", version) return + # Update try: await self.instance.update(version) except DockerAPIError: _LOGGER.error("CoreDNS update fails") raise CoreDNSUpdateError() from None - else: - # Cleanup - with suppress(DockerAPIError): - await self.instance.cleanup() + + # Cleanup + with suppress(DockerAPIError): + await self.instance.cleanup() self.version = version self.save_data() diff --git a/supervisor/host/info.py b/supervisor/host/info.py index cc964e899..58a1ee544 100644 --- a/supervisor/host/info.py +++ b/supervisor/host/info.py @@ -1,9 +1,15 @@ """Info control for host.""" +import asyncio import logging from typing import Optional from ..coresys import CoreSysAttributes -from ..exceptions import HostNotSupportedError, DBusNotConnectedError, DBusError +from ..exceptions import ( + HostNotSupportedError, + HostError, + DBusNotConnectedError, + DBusError, +) _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -45,6 +51,21 @@ class InfoCenter(CoreSysAttributes): """Return local CPE.""" return self.sys_dbus.hostname.cpe + async def get_dmesg(self) -> bytes: + """Return host dmesg output.""" + proc = await asyncio.create_subprocess_shell( + "dmesg", stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT + ) + + # Get kernel log + try: + stdout, _ = await proc.communicate() + except OSError as err: + _LOGGER.error("Can't read kernel log: %s", err) + raise HostError() + + return stdout + async def update(self): """Update properties over dbus.""" _LOGGER.info("Update local host information")