Add diagnostics support (#1870)

* Add diagnostics support

Signed-off-by: Pascal Vizeli <pvizeli@syshack.ch>

* add aditional data

* Fix handling

* Better states

* Fix opt

* Update supervisor/bootstrap.py

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>

* Only events on supported systems

Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
Pascal Vizeli 2020-08-05 14:54:03 +02:00 committed by GitHub
parent 6599ae0ee0
commit ad988f2a24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 91 additions and 2 deletions

1
API.md
View File

@ -47,6 +47,7 @@ The addons from `addons` are only installed one.
"wait_boot": "int", "wait_boot": "int",
"debug": "bool", "debug": "bool",
"debug_block": "bool", "debug_block": "bool",
"diagnostics": "None|bool",
"addons": [ "addons": [
{ {
"name": "xy bla", "name": "xy bla",

View File

@ -14,5 +14,6 @@ pulsectl==20.5.1
pytz==2020.1 pytz==2020.1
pyudev==0.22.0 pyudev==0.22.0
ruamel.yaml==0.15.100 ruamel.yaml==0.15.100
sentry-sdk==0.16.3
uvloop==0.14.0 uvloop==0.14.0
voluptuous==0.11.7 voluptuous==0.11.7

View File

@ -17,6 +17,7 @@ from ..const import (
ATTR_DEBUG, ATTR_DEBUG,
ATTR_DEBUG_BLOCK, ATTR_DEBUG_BLOCK,
ATTR_DESCRIPTON, ATTR_DESCRIPTON,
ATTR_DIAGNOSTICS,
ATTR_ICON, ATTR_ICON,
ATTR_INSTALLED, ATTR_INSTALLED,
ATTR_IP_ADDRESS, ATTR_IP_ADDRESS,
@ -58,6 +59,7 @@ SCHEMA_OPTIONS = vol.Schema(
vol.Optional(ATTR_LOGGING): vol.Coerce(LogLevel), vol.Optional(ATTR_LOGGING): vol.Coerce(LogLevel),
vol.Optional(ATTR_DEBUG): vol.Boolean(), vol.Optional(ATTR_DEBUG): vol.Boolean(),
vol.Optional(ATTR_DEBUG_BLOCK): vol.Boolean(), vol.Optional(ATTR_DEBUG_BLOCK): vol.Boolean(),
vol.Optional(ATTR_DIAGNOSTICS): vol.Boolean(),
} }
) )
@ -102,6 +104,7 @@ class APISupervisor(CoreSysAttributes):
ATTR_LOGGING: self.sys_config.logging, ATTR_LOGGING: self.sys_config.logging,
ATTR_DEBUG: self.sys_config.debug, ATTR_DEBUG: self.sys_config.debug,
ATTR_DEBUG_BLOCK: self.sys_config.debug_block, ATTR_DEBUG_BLOCK: self.sys_config.debug_block,
ATTR_DIAGNOSTICS: self.sys_config.diagnostics,
ATTR_ADDONS: list_addons, ATTR_ADDONS: list_addons,
ATTR_ADDONS_REPOSITORIES: self.sys_config.addons_repositories, ATTR_ADDONS_REPOSITORIES: self.sys_config.addons_repositories,
} }
@ -126,6 +129,9 @@ class APISupervisor(CoreSysAttributes):
if ATTR_DEBUG_BLOCK in body: if ATTR_DEBUG_BLOCK in body:
self.sys_config.debug_block = body[ATTR_DEBUG_BLOCK] self.sys_config.debug_block = body[ATTR_DEBUG_BLOCK]
if ATTR_DIAGNOSTICS in body:
self.sys_config.diagnostics = body[ATTR_DIAGNOSTICS]
if ATTR_LOGGING in body: if ATTR_LOGGING in body:
self.sys_config.logging = body[ATTR_LOGGING] self.sys_config.logging = body[ATTR_LOGGING]

View File

@ -6,6 +6,13 @@ import shutil
import signal import signal
from colorlog import ColoredFormatter from colorlog import ColoredFormatter
import sentry_sdk
from sentry_sdk.integrations.aiohttp import AioHttpIntegration
from sentry_sdk.integrations.atexit import AtexitIntegration
from sentry_sdk.integrations.dedupe import DedupeIntegration
from sentry_sdk.integrations.excepthook import ExcepthookIntegration
from sentry_sdk.integrations.stdlib import StdlibIntegration
from sentry_sdk.integrations.threading import ThreadingIntegration
from .addons import AddonManager from .addons import AddonManager
from .api import RestAPI from .api import RestAPI
@ -17,6 +24,8 @@ from .const import (
ENV_SUPERVISOR_NAME, ENV_SUPERVISOR_NAME,
ENV_SUPERVISOR_SHARE, ENV_SUPERVISOR_SHARE,
SOCKET_DOCKER, SOCKET_DOCKER,
SUPERVISOR_VERSION,
CoreStates,
LogLevel, LogLevel,
UpdateChannels, UpdateChannels,
) )
@ -73,6 +82,9 @@ async def initialize_coresys() -> CoreSys:
coresys.secrets = SecretsManager(coresys) coresys.secrets = SecretsManager(coresys)
coresys.scheduler = Scheduler(coresys) coresys.scheduler = Scheduler(coresys)
# diagnostics
setup_diagnostics(coresys)
# bootstrap config # bootstrap config
initialize_system_data(coresys) initialize_system_data(coresys)
@ -270,3 +282,54 @@ def supervisor_debugger(coresys: CoreSys) -> None:
if coresys.config.debug_block: if coresys.config.debug_block:
_LOGGER.info("Wait until debugger is attached") _LOGGER.info("Wait until debugger is attached")
ptvsd.wait_for_attach() ptvsd.wait_for_attach()
def setup_diagnostics(coresys: CoreSys) -> None:
"""Sentry diagnostic backend."""
def filter_data(event, hint):
# Ignore issue if system is not supported or diagnostics is disabled
if not coresys.config.diagnostics or not coresys.core.healthy:
return None
# Not full startup - missing information
if coresys.core.state in (CoreStates.INITIALIZE, CoreStates.SETUP):
return event
# Update information
with sentry_sdk.configure_scope() as scope:
scope.set_context(
"supervisor",
{
"machine": coresys.machine,
"arch": coresys.arch.default,
"docker": coresys.docker.info.version,
"channel": coresys.updater.channel,
"supervisor": coresys.supervisor.version,
"os": coresys.hassos.version,
"core": coresys.homeassistant.version,
"audio": coresys.plugins.audio.version,
"dns": coresys.plugins.dns.version,
"multicast": coresys.plugins.multicast.version,
"cli": coresys.plugins.cli.version,
},
)
return event
sentry_sdk.init(
dsn="https://9c6ea70f49234442b4746e447b24747e@o427061.ingest.sentry.io/5370612",
before_send=filter_data,
default_integrations=False,
integrations=[
AioHttpIntegration(),
AtexitIntegration(),
ExcepthookIntegration(),
DedupeIntegration(),
StdlibIntegration(),
ThreadingIntegration(),
],
)
with sentry_sdk.configure_scope() as scope:
scope.set_tag("version", SUPERVISOR_VERSION)

View File

@ -3,12 +3,13 @@ from datetime import datetime
import logging import logging
import os import os
from pathlib import Path, PurePath from pathlib import Path, PurePath
from typing import List from typing import List, Optional
from .const import ( from .const import (
ATTR_ADDONS_CUSTOM_LIST, ATTR_ADDONS_CUSTOM_LIST,
ATTR_DEBUG, ATTR_DEBUG,
ATTR_DEBUG_BLOCK, ATTR_DEBUG_BLOCK,
ATTR_DIAGNOSTICS,
ATTR_LAST_BOOT, ATTR_LAST_BOOT,
ATTR_LOGGING, ATTR_LOGGING,
ATTR_TIMEZONE, ATTR_TIMEZONE,
@ -101,6 +102,16 @@ class CoreConfig(JsonConfig):
"""Set debug wait mode.""" """Set debug wait mode."""
self._data[ATTR_DEBUG_BLOCK] = value self._data[ATTR_DEBUG_BLOCK] = value
@property
def diagnostics(self) -> Optional[bool]:
"""Return bool if diagnostics is set otherwise None."""
return self._data[ATTR_DIAGNOSTICS]
@diagnostics.setter
def diagnostics(self, value: bool) -> None:
"""Set diagnostics settings."""
self._data[ATTR_DIAGNOSTICS] = value
@property @property
def logging(self) -> LogLevel: def logging(self) -> LogLevel:
"""Return log level of system.""" """Return log level of system."""

View File

@ -240,6 +240,7 @@ ATTR_INDEX = "index"
ATTR_ACTIVE = "active" ATTR_ACTIVE = "active"
ATTR_APPLICATION = "application" ATTR_APPLICATION = "application"
ATTR_INIT = "init" ATTR_INIT = "init"
ATTR_DIAGNOSTICS = "diagnostics"
PROVIDE_SERVICE = "provide" PROVIDE_SERVICE = "provide"
NEED_SERVICE = "need" NEED_SERVICE = "need"
@ -355,6 +356,7 @@ class CoreStates(str, Enum):
"""Represent current loading state.""" """Represent current loading state."""
INITIALIZE = "initialize" INITIALIZE = "initialize"
SETUP = "setup"
STARTUP = "startup" STARTUP = "startup"
RUNNING = "running" RUNNING = "running"
FREEZE = "freeze" FREEZE = "freeze"

View File

@ -55,7 +55,7 @@ class Core(CoreSysAttributes):
async def setup(self): async def setup(self):
"""Start setting up supervisor orchestration.""" """Start setting up supervisor orchestration."""
self.state = CoreStates.STARTUP self.state = CoreStates.SETUP
# Load DBus # Load DBus
await self.sys_dbus.load() await self.sys_dbus.load()
@ -104,6 +104,7 @@ class Core(CoreSysAttributes):
async def start(self): async def start(self):
"""Start Supervisor orchestration.""" """Start Supervisor orchestration."""
self.state = CoreStates.STARTUP
await self.sys_api.start() await self.sys_api.start()
# Mark booted partition as healthy # Mark booted partition as healthy

View File

@ -18,6 +18,7 @@ from .const import (
ATTR_CLI, ATTR_CLI,
ATTR_DEBUG, ATTR_DEBUG,
ATTR_DEBUG_BLOCK, ATTR_DEBUG_BLOCK,
ATTR_DIAGNOSTICS,
ATTR_DNS, ATTR_DNS,
ATTR_HASSOS, ATTR_HASSOS,
ATTR_HOMEASSISTANT, ATTR_HOMEASSISTANT,
@ -176,6 +177,7 @@ SCHEMA_SUPERVISOR_CONFIG = vol.Schema(
vol.Optional(ATTR_LOGGING, default=LogLevel.INFO): vol.Coerce(LogLevel), vol.Optional(ATTR_LOGGING, default=LogLevel.INFO): vol.Coerce(LogLevel),
vol.Optional(ATTR_DEBUG, default=False): vol.Boolean(), vol.Optional(ATTR_DEBUG, default=False): vol.Boolean(),
vol.Optional(ATTR_DEBUG_BLOCK, default=False): vol.Boolean(), vol.Optional(ATTR_DEBUG_BLOCK, default=False): vol.Boolean(),
vol.Optional(ATTR_DIAGNOSTICS, default=None): vol.Maybe(vol.Boolean()),
}, },
extra=vol.REMOVE_EXTRA, extra=vol.REMOVE_EXTRA,
) )

View File

@ -19,6 +19,8 @@ def docker():
async def coresys(loop, docker): async def coresys(loop, docker):
"""Create a CoreSys Mock.""" """Create a CoreSys Mock."""
with patch("supervisor.bootstrap.initialize_system_data"), patch( with patch("supervisor.bootstrap.initialize_system_data"), patch(
"supervisor.bootstrap.setup_diagnostics"
), patch(
"supervisor.bootstrap.fetch_timezone", return_value="Europe/Zurich", "supervisor.bootstrap.fetch_timezone", return_value="Europe/Zurich",
): ):
coresys_obj = await initialize_coresys() coresys_obj = await initialize_coresys()