mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-13 04:06:33 +00:00
Use lock on homeassistant level
This commit is contained in:
parent
1efdcd4691
commit
c2918d4519
@ -6,11 +6,11 @@ import docker
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
from .interface import DockerInterface
|
from .interface import DockerInterface
|
||||||
from .utils import docker_process
|
|
||||||
from ..addons.build import AddonBuild
|
from ..addons.build import AddonBuild
|
||||||
from ..const import (
|
from ..const import (
|
||||||
MAP_CONFIG, MAP_SSL, MAP_ADDONS, MAP_BACKUP, MAP_SHARE, ENV_TOKEN,
|
MAP_CONFIG, MAP_SSL, MAP_ADDONS, MAP_BACKUP, MAP_SHARE, ENV_TOKEN,
|
||||||
ENV_TIME)
|
ENV_TIME)
|
||||||
|
from ..utils import process_lock
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -285,7 +285,7 @@ class DockerAddon(DockerInterface):
|
|||||||
_LOGGER.info("Build %s:%s done", self.image, tag)
|
_LOGGER.info("Build %s:%s done", self.image, tag)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@docker_process
|
@process_lock
|
||||||
def export_image(self, path):
|
def export_image(self, path):
|
||||||
"""Export current images into a tar file."""
|
"""Export current images into a tar file."""
|
||||||
return self._loop.run_in_executor(None, self._export_image, path)
|
return self._loop.run_in_executor(None, self._export_image, path)
|
||||||
@ -313,7 +313,7 @@ class DockerAddon(DockerInterface):
|
|||||||
_LOGGER.info("Export image %s done", self.image)
|
_LOGGER.info("Export image %s done", self.image)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@docker_process
|
@process_lock
|
||||||
def import_image(self, path, tag):
|
def import_image(self, path, tag):
|
||||||
"""Import a tar file as image."""
|
"""Import a tar file as image."""
|
||||||
return self._loop.run_in_executor(None, self._import_image, path, tag)
|
return self._loop.run_in_executor(None, self._import_image, path, tag)
|
||||||
@ -338,7 +338,7 @@ class DockerAddon(DockerInterface):
|
|||||||
self._cleanup()
|
self._cleanup()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@docker_process
|
@process_lock
|
||||||
def write_stdin(self, data):
|
def write_stdin(self, data):
|
||||||
"""Write to add-on stdin."""
|
"""Write to add-on stdin."""
|
||||||
return self._loop.run_in_executor(None, self._write_stdin, data)
|
return self._loop.run_in_executor(None, self._write_stdin, data)
|
||||||
|
@ -5,10 +5,10 @@ import logging
|
|||||||
|
|
||||||
import docker
|
import docker
|
||||||
|
|
||||||
from .utils import docker_process
|
|
||||||
from .stats import DockerStats
|
from .stats import DockerStats
|
||||||
from ..const import LABEL_VERSION, LABEL_ARCH
|
from ..const import LABEL_VERSION, LABEL_ARCH
|
||||||
from ..coresys import CoreSysAttributes
|
from ..coresys import CoreSysAttributes
|
||||||
|
from ..utils import process_lock
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ class DockerInterface(CoreSysAttributes):
|
|||||||
"""Initialize docker base wrapper."""
|
"""Initialize docker base wrapper."""
|
||||||
self.coresys = coresys
|
self.coresys = coresys
|
||||||
self._meta = None
|
self._meta = None
|
||||||
self.lock = asyncio.Lock(loop=self._loop)
|
self.lock = asyncio.Lock(loop=coresys.loop)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def timeout(self):
|
def timeout(self):
|
||||||
@ -58,7 +58,7 @@ class DockerInterface(CoreSysAttributes):
|
|||||||
"""Return True if a task is in progress."""
|
"""Return True if a task is in progress."""
|
||||||
return self.lock.locked()
|
return self.lock.locked()
|
||||||
|
|
||||||
@docker_process
|
@process_lock
|
||||||
def install(self, tag):
|
def install(self, tag):
|
||||||
"""Pull docker image."""
|
"""Pull docker image."""
|
||||||
return self._loop.run_in_executor(None, self._install, tag)
|
return self._loop.run_in_executor(None, self._install, tag)
|
||||||
@ -126,7 +126,7 @@ class DockerInterface(CoreSysAttributes):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@docker_process
|
@process_lock
|
||||||
def attach(self):
|
def attach(self):
|
||||||
"""Attach to running docker container."""
|
"""Attach to running docker container."""
|
||||||
return self._loop.run_in_executor(None, self._attach)
|
return self._loop.run_in_executor(None, self._attach)
|
||||||
@ -149,7 +149,7 @@ class DockerInterface(CoreSysAttributes):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@docker_process
|
@process_lock
|
||||||
def run(self):
|
def run(self):
|
||||||
"""Run docker image."""
|
"""Run docker image."""
|
||||||
return self._loop.run_in_executor(None, self._run)
|
return self._loop.run_in_executor(None, self._run)
|
||||||
@ -161,7 +161,7 @@ class DockerInterface(CoreSysAttributes):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@docker_process
|
@process_lock
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""Stop/remove docker container."""
|
"""Stop/remove docker container."""
|
||||||
return self._loop.run_in_executor(None, self._stop)
|
return self._loop.run_in_executor(None, self._stop)
|
||||||
@ -187,7 +187,7 @@ class DockerInterface(CoreSysAttributes):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@docker_process
|
@process_lock
|
||||||
def remove(self):
|
def remove(self):
|
||||||
"""Remove docker images."""
|
"""Remove docker images."""
|
||||||
return self._loop.run_in_executor(None, self._remove)
|
return self._loop.run_in_executor(None, self._remove)
|
||||||
@ -219,7 +219,7 @@ class DockerInterface(CoreSysAttributes):
|
|||||||
self._meta = None
|
self._meta = None
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@docker_process
|
@process_lock
|
||||||
def update(self, tag):
|
def update(self, tag):
|
||||||
"""Update a docker image."""
|
"""Update a docker image."""
|
||||||
return self._loop.run_in_executor(None, self._update, tag)
|
return self._loop.run_in_executor(None, self._update, tag)
|
||||||
@ -264,7 +264,7 @@ class DockerInterface(CoreSysAttributes):
|
|||||||
except docker.errors.DockerException as err:
|
except docker.errors.DockerException as err:
|
||||||
_LOGGER.warning("Can't grap logs from %s: %s", self.image, err)
|
_LOGGER.warning("Can't grap logs from %s: %s", self.image, err)
|
||||||
|
|
||||||
@docker_process
|
@process_lock
|
||||||
def restart(self):
|
def restart(self):
|
||||||
"""Restart docker container."""
|
"""Restart docker container."""
|
||||||
return self._loop.run_in_executor(None, self._restart)
|
return self._loop.run_in_executor(None, self._restart)
|
||||||
@ -289,7 +289,7 @@ class DockerInterface(CoreSysAttributes):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@docker_process
|
@process_lock
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
"""Check if old version exists and cleanup."""
|
"""Check if old version exists and cleanup."""
|
||||||
return self._loop.run_in_executor(None, self._cleanup)
|
return self._loop.run_in_executor(None, self._cleanup)
|
||||||
@ -315,7 +315,7 @@ class DockerInterface(CoreSysAttributes):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@docker_process
|
@process_lock
|
||||||
def execute_command(self, command):
|
def execute_command(self, command):
|
||||||
"""Create a temporary container and run command."""
|
"""Create a temporary container and run command."""
|
||||||
return self._loop.run_in_executor(None, self._execute_command, command)
|
return self._loop.run_in_executor(None, self._execute_command, command)
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
"""HassIO docker utilitys."""
|
|
||||||
import logging
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=protected-access
|
|
||||||
def docker_process(method):
|
|
||||||
"""Wrap function with only run once."""
|
|
||||||
async def wrap_api(api, *args, **kwargs):
|
|
||||||
"""Return api wrapper."""
|
|
||||||
if api.lock.locked():
|
|
||||||
_LOGGER.error(
|
|
||||||
"Can't excute %s while a task is in progress", method.__name__)
|
|
||||||
return False
|
|
||||||
|
|
||||||
async with api.lock:
|
|
||||||
return await method(api, *args, **kwargs)
|
|
||||||
|
|
||||||
return wrap_api
|
|
@ -16,7 +16,7 @@ from .const import (
|
|||||||
ATTR_WAIT_BOOT, HEADER_HA_ACCESS, CONTENT_TYPE_JSON)
|
ATTR_WAIT_BOOT, HEADER_HA_ACCESS, CONTENT_TYPE_JSON)
|
||||||
from .coresys import CoreSysAttributes
|
from .coresys import CoreSysAttributes
|
||||||
from .docker.homeassistant import DockerHomeAssistant
|
from .docker.homeassistant import DockerHomeAssistant
|
||||||
from .utils import convert_to_ascii
|
from .utils import convert_to_ascii, process_lock
|
||||||
from .utils.json import JsonConfig
|
from .utils.json import JsonConfig
|
||||||
from .validate import SCHEMA_HASS_CONFIG
|
from .validate import SCHEMA_HASS_CONFIG
|
||||||
|
|
||||||
@ -35,6 +35,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
super().__init__(FILE_HASSIO_HOMEASSISTANT, SCHEMA_HASS_CONFIG)
|
super().__init__(FILE_HASSIO_HOMEASSISTANT, SCHEMA_HASS_CONFIG)
|
||||||
self.coresys = coresys
|
self.coresys = coresys
|
||||||
self.instance = DockerHomeAssistant(coresys)
|
self.instance = DockerHomeAssistant(coresys)
|
||||||
|
self.lock = asyncio.Lock(loop=coresys.loop)
|
||||||
|
|
||||||
async def load(self):
|
async def load(self):
|
||||||
"""Prepare HomeAssistant object."""
|
"""Prepare HomeAssistant object."""
|
||||||
@ -162,6 +163,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
"""Return a UUID of this HomeAssistant."""
|
"""Return a UUID of this HomeAssistant."""
|
||||||
return self._data[ATTR_UUID]
|
return self._data[ATTR_UUID]
|
||||||
|
|
||||||
|
@process_lock
|
||||||
async def install_landingpage(self):
|
async def install_landingpage(self):
|
||||||
"""Install a landingpage."""
|
"""Install a landingpage."""
|
||||||
_LOGGER.info("Setup HomeAssistant landingpage")
|
_LOGGER.info("Setup HomeAssistant landingpage")
|
||||||
@ -172,8 +174,10 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
await asyncio.sleep(60, loop=self._loop)
|
await asyncio.sleep(60, loop=self._loop)
|
||||||
|
|
||||||
# Run landingpage after installation
|
# Run landingpage after installation
|
||||||
await self.start()
|
await self.instance.run()
|
||||||
|
await self._block_till_run()
|
||||||
|
|
||||||
|
@process_lock
|
||||||
async def install(self):
|
async def install(self):
|
||||||
"""Install a landingpage."""
|
"""Install a landingpage."""
|
||||||
_LOGGER.info("Setup HomeAssistant")
|
_LOGGER.info("Setup HomeAssistant")
|
||||||
@ -191,9 +195,11 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
# finishing
|
# finishing
|
||||||
_LOGGER.info("HomeAssistant docker now installed")
|
_LOGGER.info("HomeAssistant docker now installed")
|
||||||
if self.boot:
|
if self.boot:
|
||||||
await self.start()
|
await self.instance.run()
|
||||||
|
await self._block_till_run()
|
||||||
await self.instance.cleanup()
|
await self.instance.cleanup()
|
||||||
|
|
||||||
|
@process_lock
|
||||||
async def update(self, version=None):
|
async def update(self, version=None):
|
||||||
"""Update HomeAssistant version."""
|
"""Update HomeAssistant version."""
|
||||||
version = version or self.last_version
|
version = version or self.last_version
|
||||||
@ -208,8 +214,10 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
return await self.instance.update(version)
|
return await self.instance.update(version)
|
||||||
finally:
|
finally:
|
||||||
if running:
|
if running:
|
||||||
await self.start()
|
await self.instance.run()
|
||||||
|
await self._block_till_run()
|
||||||
|
|
||||||
|
@process_lock
|
||||||
async def start(self):
|
async def start(self):
|
||||||
"""Run HomeAssistant docker."""
|
"""Run HomeAssistant docker."""
|
||||||
if not await self.instance.run():
|
if not await self.instance.run():
|
||||||
@ -224,6 +232,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
"""
|
"""
|
||||||
return self.instance.stop()
|
return self.instance.stop()
|
||||||
|
|
||||||
|
@process_lock
|
||||||
async def restart(self):
|
async def restart(self):
|
||||||
"""Restart HomeAssistant docker."""
|
"""Restart HomeAssistant docker."""
|
||||||
if not await self.instance.restart():
|
if not await self.instance.restart():
|
||||||
@ -262,7 +271,7 @@ class HomeAssistant(JsonConfig, CoreSysAttributes):
|
|||||||
@property
|
@property
|
||||||
def in_progress(self):
|
def in_progress(self):
|
||||||
"""Return True if a task is in progress."""
|
"""Return True if a task is in progress."""
|
||||||
return self.instance.in_progress
|
return self.instance.in_progress or self.lock.locked()
|
||||||
|
|
||||||
async def check_config(self):
|
async def check_config(self):
|
||||||
"""Run homeassistant config check."""
|
"""Run homeassistant config check."""
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
"""Tools file for HassIO."""
|
"""Tools file for HassIO."""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
RE_STRING = re.compile(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))")
|
RE_STRING = re.compile(r"\x1b(\[.*?[@-~]|\].*?(\x07|\x1b\\))")
|
||||||
|
|
||||||
|
|
||||||
@ -10,6 +12,21 @@ def convert_to_ascii(raw):
|
|||||||
return RE_STRING.sub("", raw.decode())
|
return RE_STRING.sub("", raw.decode())
|
||||||
|
|
||||||
|
|
||||||
|
def process_lock(method):
|
||||||
|
"""Wrap function with only run once."""
|
||||||
|
async def wrap_api(api, *args, **kwargs):
|
||||||
|
"""Return api wrapper."""
|
||||||
|
if api.lock.locked():
|
||||||
|
_LOGGER.error(
|
||||||
|
"Can't excute %s while a task is in progress", method.__name__)
|
||||||
|
return False
|
||||||
|
|
||||||
|
async with api.lock:
|
||||||
|
return await method(api, *args, **kwargs)
|
||||||
|
|
||||||
|
return wrap_api
|
||||||
|
|
||||||
|
|
||||||
class AsyncThrottle(object):
|
class AsyncThrottle(object):
|
||||||
"""
|
"""
|
||||||
Decorator that prevents a function from being called more than once every
|
Decorator that prevents a function from being called more than once every
|
||||||
|
106
setup.py
106
setup.py
@ -1,53 +1,53 @@
|
|||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
from hassio.const import HASSIO_VERSION
|
from hassio.const import HASSIO_VERSION
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='HassIO',
|
name='HassIO',
|
||||||
version=HASSIO_VERSION,
|
version=HASSIO_VERSION,
|
||||||
license='BSD License',
|
license='BSD License',
|
||||||
author='The Home Assistant Authors',
|
author='The Home Assistant Authors',
|
||||||
author_email='hello@home-assistant.io',
|
author_email='hello@home-assistant.io',
|
||||||
url='https://home-assistant.io/',
|
url='https://home-assistant.io/',
|
||||||
description=('Open-source private cloud os for Home-Assistant'
|
description=('Open-source private cloud os for Home-Assistant'
|
||||||
' based on ResinOS'),
|
' based on ResinOS'),
|
||||||
long_description=('A maintainless private cloud operator system that'
|
long_description=('A maintainless private cloud operator system that'
|
||||||
'setup a Home-Assistant instance. Based on ResinOS'),
|
'setup a Home-Assistant instance. Based on ResinOS'),
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Intended Audience :: End Users/Desktop',
|
'Intended Audience :: End Users/Desktop',
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
'License :: OSI Approved :: Apache Software License',
|
'License :: OSI Approved :: Apache Software License',
|
||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
'Topic :: Home Automation'
|
'Topic :: Home Automation'
|
||||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||||
'Topic :: Scientific/Engineering :: Atmospheric Science',
|
'Topic :: Scientific/Engineering :: Atmospheric Science',
|
||||||
'Development Status :: 5 - Production/Stable',
|
'Development Status :: 5 - Production/Stable',
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
'Programming Language :: Python :: 3.6',
|
'Programming Language :: Python :: 3.6',
|
||||||
],
|
],
|
||||||
keywords=['docker', 'home-assistant', 'api'],
|
keywords=['docker', 'home-assistant', 'api'],
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
platforms='any',
|
platforms='any',
|
||||||
packages=[
|
packages=[
|
||||||
'hassio',
|
'hassio',
|
||||||
'hassio.docker',
|
'hassio.docker',
|
||||||
'hassio.addons',
|
'hassio.addons',
|
||||||
'hassio.api',
|
'hassio.api',
|
||||||
'hassio.misc',
|
'hassio.misc',
|
||||||
'hassio.utils',
|
'hassio.utils',
|
||||||
'hassio.snapshots'
|
'hassio.snapshots'
|
||||||
],
|
],
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'async_timeout==2.0.0',
|
'async_timeout==2.0.0',
|
||||||
'aiohttp==3.0.7',
|
'aiohttp==3.0.7',
|
||||||
'docker==3.1.1',
|
'docker==3.1.1',
|
||||||
'colorlog==3.1.2',
|
'colorlog==3.1.2',
|
||||||
'voluptuous==0.11.1',
|
'voluptuous==0.11.1',
|
||||||
'gitpython==2.1.8',
|
'gitpython==2.1.8',
|
||||||
'pytz==2018.3',
|
'pytz==2018.3',
|
||||||
'pyudev==0.21.0',
|
'pyudev==0.21.0',
|
||||||
'pycryptodome==3.4.11'
|
'pycryptodome==3.4.11'
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user