supervisor/hassio/bootstrap.py
Pascal Vizeli af19e95c81
Make discovery persistent (#727)
* Make discovery persistent

* fix file handling

* fix detection

* Smooth

* Fix ring import

* Fix handling

* fix schema

* fix validate

* fix discovery cleanup
2018-09-30 15:33:16 +02:00

204 lines
5.9 KiB
Python

"""Bootstrap Hass.io."""
import logging
import os
import signal
import shutil
from pathlib import Path
from colorlog import ColoredFormatter
from .core import HassIO
from .addons import AddonManager
from .api import RestAPI
from .const import SOCKET_DOCKER
from .coresys import CoreSys
from .supervisor import Supervisor
from .homeassistant import HomeAssistant
from .snapshots import SnapshotManager
from .tasks import Tasks
from .updater import Updater
from .services import ServiceManager
from .discovery import Discovery
from .host import HostManager
from .dbus import DBusManager
from .hassos import HassOS
_LOGGER = logging.getLogger(__name__)
ENV_SHARE = 'SUPERVISOR_SHARE'
ENV_NAME = 'SUPERVISOR_NAME'
ENV_REPO = 'HOMEASSISTANT_REPOSITORY'
MACHINE_ID = Path('/etc/machine-id')
def initialize_coresys(loop):
"""Initialize HassIO coresys/objects."""
coresys = CoreSys(loop)
# Initialize core objects
coresys.core = HassIO(coresys)
coresys.updater = Updater(coresys)
coresys.api = RestAPI(coresys)
coresys.supervisor = Supervisor(coresys)
coresys.homeassistant = HomeAssistant(coresys)
coresys.addons = AddonManager(coresys)
coresys.snapshots = SnapshotManager(coresys)
coresys.host = HostManager(coresys)
coresys.tasks = Tasks(coresys)
coresys.services = ServiceManager(coresys)
coresys.discovery = Discovery(coresys)
coresys.dbus = DBusManager(coresys)
coresys.hassos = HassOS(coresys)
# bootstrap config
initialize_system_data(coresys)
# Set Machine/Host ID
if MACHINE_ID.exists():
coresys.machine_id = MACHINE_ID.read_text().strip()
return coresys
def initialize_system_data(coresys):
"""Set up the default configuration and create folders."""
config = coresys.config
# Home Assistant configuration folder
if not config.path_homeassistant.is_dir():
_LOGGER.info(
"Create Home Assistant configuration folder %s",
config.path_homeassistant)
config.path_homeassistant.mkdir()
# hassio ssl folder
if not config.path_ssl.is_dir():
_LOGGER.info("Create Hass.io SSL/TLS folder %s", config.path_ssl)
config.path_ssl.mkdir()
# hassio addon data folder
if not config.path_addons_data.is_dir():
_LOGGER.info(
"Create Hass.io Add-on data folder %s", config.path_addons_data)
config.path_addons_data.mkdir(parents=True)
if not config.path_addons_local.is_dir():
_LOGGER.info("Create Hass.io Add-on local repository folder %s",
config.path_addons_local)
config.path_addons_local.mkdir(parents=True)
if not config.path_addons_git.is_dir():
_LOGGER.info("Create Hass.io Add-on git repositories folder %s",
config.path_addons_git)
config.path_addons_git.mkdir(parents=True)
# hassio tmp folder
if not config.path_tmp.is_dir():
_LOGGER.info("Create Hass.io temp folder %s", config.path_tmp)
config.path_tmp.mkdir(parents=True)
# hassio backup folder
if not config.path_backup.is_dir():
_LOGGER.info("Create Hass.io backup folder %s", config.path_backup)
config.path_backup.mkdir()
# share folder
if not config.path_share.is_dir():
_LOGGER.info("Create Hass.io share folder %s", config.path_share)
config.path_share.mkdir()
# apparmor folder
if not config.path_apparmor.is_dir():
_LOGGER.info("Create Hass.io Apparmor folder %s", config.path_apparmor)
config.path_apparmor.mkdir()
return config
def migrate_system_env(coresys):
"""Cleanup some stuff after update."""
config = coresys.config
# hass.io 0.37 -> 0.38
old_build = Path(config.path_hassio, "addons/build")
if old_build.is_dir():
try:
old_build.rmdir()
except OSError:
_LOGGER.warning("Can't cleanup old Add-on build directory")
def initialize_logging():
"""Setup the logging."""
logging.basicConfig(level=logging.INFO)
fmt = ("%(asctime)s %(levelname)s (%(threadName)s) "
"[%(name)s] %(message)s")
colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
datefmt = '%y-%m-%d %H:%M:%S'
# suppress overly verbose logs from libraries that aren't helpful
logging.getLogger("aiohttp.access").setLevel(logging.WARNING)
logging.getLogger().handlers[0].setFormatter(ColoredFormatter(
colorfmt,
datefmt=datefmt,
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red',
}
))
def check_environment():
"""Check if all environment are exists."""
# check environment variables
for key in (ENV_SHARE, ENV_NAME, ENV_REPO):
try:
os.environ[key]
except KeyError:
_LOGGER.fatal("Can't find %s in env!", key)
return False
# check docker socket
if not SOCKET_DOCKER.is_socket():
_LOGGER.fatal("Can't find Docker socket!")
return False
# check socat exec
if not shutil.which('socat'):
_LOGGER.fatal("Can't find socat!")
return False
# check socat exec
if not shutil.which('gdbus'):
_LOGGER.fatal("Can't find gdbus!")
return False
return True
def reg_signal(loop):
"""Register SIGTERM and SIGKILL to stop system."""
try:
loop.add_signal_handler(
signal.SIGTERM, lambda: loop.call_soon(loop.stop))
except (ValueError, RuntimeError):
_LOGGER.warning("Could not bind to SIGTERM")
try:
loop.add_signal_handler(
signal.SIGHUP, lambda: loop.call_soon(loop.stop))
except (ValueError, RuntimeError):
_LOGGER.warning("Could not bind to SIGHUP")
try:
loop.add_signal_handler(
signal.SIGINT, lambda: loop.call_soon(loop.stop))
except (ValueError, RuntimeError):
_LOGGER.warning("Could not bind to SIGINT")