Change startup system / Allow nested dict (#99)

* Change startup system / Allow nested dict

* fix schema

* fix lint
This commit is contained in:
Pascal Vizeli 2017-07-14 23:51:24 +02:00 committed by GitHub
parent fa049066fc
commit 143a358b0c
3 changed files with 63 additions and 25 deletions

View File

@ -3,13 +3,14 @@ import voluptuous as vol
from ..const import ( from ..const import (
ATTR_NAME, ATTR_VERSION, ATTR_SLUG, ATTR_DESCRIPTON, ATTR_STARTUP, ATTR_NAME, ATTR_VERSION, ATTR_SLUG, ATTR_DESCRIPTON, ATTR_STARTUP,
ATTR_BOOT, ATTR_MAP, ATTR_OPTIONS, ATTR_PORTS, STARTUP_ONCE, STARTUP_AFTER, ATTR_BOOT, ATTR_MAP, ATTR_OPTIONS, ATTR_PORTS, STARTUP_ONCE,
STARTUP_BEFORE, STARTUP_INITIALIZE, BOOT_AUTO, BOOT_MANUAL, ATTR_SCHEMA, STARTUP_SYSTEM, STARTUP_SERVICES, STARTUP_APPLICATION, STARTUP_INITIALIZE,
ATTR_IMAGE, ATTR_URL, ATTR_MAINTAINER, ATTR_ARCH, ATTR_DEVICES, BOOT_AUTO, BOOT_MANUAL, ATTR_SCHEMA, ATTR_IMAGE, ATTR_URL, ATTR_MAINTAINER,
ATTR_ENVIRONMENT, ATTR_HOST_NETWORK, ARCH_ARMHF, ARCH_AARCH64, ARCH_AMD64, ATTR_ARCH, ATTR_DEVICES, ATTR_ENVIRONMENT, ATTR_HOST_NETWORK, ARCH_ARMHF,
ARCH_I386, ATTR_TMPFS, ATTR_PRIVILEGED, ATTR_USER, ATTR_STATE, ATTR_SYSTEM, ARCH_AARCH64, ARCH_AMD64, ARCH_I386, ATTR_TMPFS, ATTR_PRIVILEGED,
STATE_STARTED, STATE_STOPPED, ATTR_LOCATON, ATTR_REPOSITORY, ATTR_TIMEOUT, ATTR_USER, ATTR_STATE, ATTR_SYSTEM, STATE_STARTED, STATE_STOPPED,
ATTR_NETWORK, ATTR_AUTO_UPDATE) ATTR_LOCATON, ATTR_REPOSITORY, ATTR_TIMEOUT, ATTR_NETWORK,
ATTR_AUTO_UPDATE)
from ..validate import NETWORK_PORT, DOCKER_PORTS from ..validate import NETWORK_PORT, DOCKER_PORTS
@ -29,11 +30,28 @@ ARCH_ALL = [
ARCH_ARMHF, ARCH_AARCH64, ARCH_AMD64, ARCH_I386 ARCH_ARMHF, ARCH_AARCH64, ARCH_AMD64, ARCH_I386
] ]
STARTUP_ALL = [
STARTUP_ONCE, STARTUP_INITIALIZE, STARTUP_SYSTEM, STARTUP_SERVICES,
STARTUP_APPLICATION
]
PRIVILEGE_ALL = [ PRIVILEGE_ALL = [
"NET_ADMIN" "NET_ADMIN"
] ]
def _migrate_startup(value):
"""Migrate startup schema.
REMOVE after 0.50-
"""
if value == "before":
return STARTUP_SERVICES
if value == "after":
return STARTUP_APPLICATION
return value
# pylint: disable=no-value-for-parameter # pylint: disable=no-value-for-parameter
SCHEMA_ADDON_CONFIG = vol.Schema({ SCHEMA_ADDON_CONFIG = vol.Schema({
vol.Required(ATTR_NAME): vol.Coerce(str), vol.Required(ATTR_NAME): vol.Coerce(str),
@ -43,8 +61,7 @@ SCHEMA_ADDON_CONFIG = vol.Schema({
vol.Optional(ATTR_URL): vol.Url(), vol.Optional(ATTR_URL): vol.Url(),
vol.Optional(ATTR_ARCH, default=ARCH_ALL): [vol.In(ARCH_ALL)], vol.Optional(ATTR_ARCH, default=ARCH_ALL): [vol.In(ARCH_ALL)],
vol.Required(ATTR_STARTUP): vol.Required(ATTR_STARTUP):
vol.In([STARTUP_BEFORE, STARTUP_AFTER, STARTUP_ONCE, vol.All(_migrate_startup, vol.In([STARTUP_ALL])),
STARTUP_INITIALIZE]),
vol.Required(ATTR_BOOT): vol.Required(ATTR_BOOT):
vol.In([BOOT_AUTO, BOOT_MANUAL]), vol.In([BOOT_AUTO, BOOT_MANUAL]),
vol.Optional(ATTR_PORTS): DOCKER_PORTS, vol.Optional(ATTR_PORTS): DOCKER_PORTS,
@ -56,11 +73,11 @@ SCHEMA_ADDON_CONFIG = vol.Schema({
vol.Optional(ATTR_ENVIRONMENT): {vol.Match(r"\w*"): vol.Coerce(str)}, vol.Optional(ATTR_ENVIRONMENT): {vol.Match(r"\w*"): vol.Coerce(str)},
vol.Optional(ATTR_PRIVILEGED): [vol.In(PRIVILEGE_ALL)], vol.Optional(ATTR_PRIVILEGED): [vol.In(PRIVILEGE_ALL)],
vol.Required(ATTR_OPTIONS): dict, vol.Required(ATTR_OPTIONS): dict,
vol.Required(ATTR_SCHEMA): vol.Any({ vol.Required(ATTR_SCHEMA): vol.Any(vol.Schema({
vol.Coerce(str): vol.Any(ADDON_ELEMENT, [ vol.Coerce(str): vol.Any(ADDON_ELEMENT, [
vol.Any(ADDON_ELEMENT, {vol.Coerce(str): ADDON_ELEMENT}) vol.Any(ADDON_ELEMENT, {vol.Coerce(str): ADDON_ELEMENT})
]) ], vol.Schema({vol.Coerce(str): ADDON_ELEMENT}))
}, False), }), False),
vol.Optional(ATTR_IMAGE): vol.Match(r"\w*/\w*"), vol.Optional(ATTR_IMAGE): vol.Match(r"\w*/\w*"),
vol.Optional(ATTR_TIMEOUT, default=10): vol.Optional(ATTR_TIMEOUT, default=10):
vol.All(vol.Coerce(int), vol.Range(min=10, max=120)) vol.All(vol.Coerce(int), vol.Range(min=10, max=120))
@ -124,8 +141,11 @@ def validate_options(raw_schema):
typ = raw_schema[key] typ = raw_schema[key]
try: try:
if isinstance(typ, list): if isinstance(typ, list):
# nested value # nested value list
options[key] = _nested_validate(typ[0], value, key) options[key] = _nested_validate_list(typ[0], value, key)
elif isinstance(typ, dict):
# nested value dict
options[key] = _nested_validate_dict(typ, value, key)
else: else:
# normal value # normal value
options[key] = _single_validate(typ, value, key) options[key] = _single_validate(typ, value, key)
@ -161,13 +181,13 @@ def _single_validate(typ, value, key):
elif typ == V_PORT: elif typ == V_PORT:
return NETWORK_PORT(value) return NETWORK_PORT(value)
raise vol.Invalid("Fatal error for {} type {}.".format(key, typ)) raise vol.Invalid("Fatal error for {} type {}".format(key, typ))
except ValueError: except ValueError:
raise vol.Invalid( raise vol.Invalid(
"Type {} error for '{}' on {}.".format(typ, value, key)) from None "Type {} error for '{}' on {}.".format(typ, value, key)) from None
def _nested_validate(typ, data_list, key): def _nested_validate_list(typ, data_list, key):
"""Validate nested items.""" """Validate nested items."""
options = [] options = []
@ -178,7 +198,7 @@ def _nested_validate(typ, data_list, key):
for c_key, c_value in element.items(): for c_key, c_value in element.items():
if c_key not in typ: if c_key not in typ:
raise vol.Invalid( raise vol.Invalid(
"Unknown nested options {}.".format(c_key)) "Unknown nested options {}".format(c_key))
c_options[c_key] = _single_validate(typ[c_key], c_value, c_key) c_options[c_key] = _single_validate(typ[c_key], c_value, c_key)
options.append(c_options) options.append(c_options)
@ -187,3 +207,16 @@ def _nested_validate(typ, data_list, key):
options.append(_single_validate(typ, element, key)) options.append(_single_validate(typ, element, key))
return options return options
def _nested_validate_dict(typ, data_dict, key):
"""Validate nested items."""
options = {}
for c_key, c_value in data_dict.items():
if c_key not in typ:
raise vol.Invalid("Unknow nested dict options {}".format(c_key))
options[c_key] = _single_validate(typ[c_key], c_value, c_key)
return options

View File

@ -98,8 +98,9 @@ ATTR_AUTO_UPDATE = 'auto_update'
ATTR_CUSTOM = 'custom' ATTR_CUSTOM = 'custom'
STARTUP_INITIALIZE = 'initialize' STARTUP_INITIALIZE = 'initialize'
STARTUP_BEFORE = 'before' STARTUP_SYSTEM = 'system'
STARTUP_AFTER = 'after' STARTUP_SERVICES = 'services'
STARTUP_APPLICATION = 'application'
STARTUP_ONCE = 'once' STARTUP_ONCE = 'once'
BOOT_AUTO = 'auto' BOOT_AUTO = 'auto'

View File

@ -11,8 +11,9 @@ from .host_control import HostControl
from .const import ( from .const import (
SOCKET_DOCKER, RUN_UPDATE_INFO_TASKS, RUN_RELOAD_ADDONS_TASKS, SOCKET_DOCKER, RUN_UPDATE_INFO_TASKS, RUN_RELOAD_ADDONS_TASKS,
RUN_UPDATE_SUPERVISOR_TASKS, RUN_WATCHDOG_HOMEASSISTANT, RUN_UPDATE_SUPERVISOR_TASKS, RUN_WATCHDOG_HOMEASSISTANT,
RUN_CLEANUP_API_SESSIONS, STARTUP_AFTER, STARTUP_BEFORE, RUN_CLEANUP_API_SESSIONS, STARTUP_SYSTEM, STARTUP_SERVICES,
STARTUP_INITIALIZE, RUN_RELOAD_SNAPSHOTS_TASKS, RUN_UPDATE_ADDONS_TASKS) STARTUP_APPLICATION, STARTUP_INITIALIZE, RUN_RELOAD_SNAPSHOTS_TASKS,
RUN_UPDATE_ADDONS_TASKS)
from .homeassistant import HomeAssistant from .homeassistant import HomeAssistant
from .scheduler import Scheduler from .scheduler import Scheduler
from .dock.supervisor import DockerSupervisor from .dock.supervisor import DockerSupervisor
@ -133,20 +134,23 @@ class HassIO(object):
await self.api.start() await self.api.start()
_LOGGER.info("Start hassio api on %s", self.config.api_endpoint) _LOGGER.info("Start hassio api on %s", self.config.api_endpoint)
# start addon mark as system
await self.addons.auto_boot(STARTUP_SYSTEM)
try: try:
# HomeAssistant is already running / supervisor have only reboot # HomeAssistant is already running / supervisor have only reboot
if await self.homeassistant.is_running(): if await self.homeassistant.is_running():
_LOGGER.info("HassIO reboot detected") _LOGGER.info("HassIO reboot detected")
return return
# start addon mark as before # start addon mark as services
await self.addons.auto_boot(STARTUP_BEFORE) await self.addons.auto_boot(STARTUP_SERVICES)
# run HomeAssistant # run HomeAssistant
await self.homeassistant.run() await self.homeassistant.run()
# start addon mark as after # start addon mark as application
await self.addons.auto_boot(STARTUP_AFTER) await self.addons.auto_boot(STARTUP_APPLICATION)
finally: finally:
# schedule homeassistant watchdog # schedule homeassistant watchdog