startup: Handle frontend and backend failures
This commit is contained in:
parent
cb4b23f416
commit
399124bf46
@ -58,6 +58,10 @@ class Backend(object):
|
||||
def has_playlists(self):
|
||||
return self.playlists is not None
|
||||
|
||||
def ping(self):
|
||||
"""Called to check if the actor is still alive."""
|
||||
return True
|
||||
|
||||
|
||||
class LibraryProvider(object):
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import contextlib
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
@ -232,6 +233,23 @@ class Command(object):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _actor_error_handling(name):
|
||||
try:
|
||||
yield
|
||||
except exceptions.BackendError as exc:
|
||||
logger.error(
|
||||
'Backend (%s) initialization error: %s', name, exc.message)
|
||||
except exceptions.FrontendError as exc:
|
||||
logger.error(
|
||||
'Frontend (%s) initialization error: %s', name, exc.message)
|
||||
except exceptions.MixerError as exc:
|
||||
logger.error(
|
||||
'Mixer (%s) initialization error: %s', name, exc.message)
|
||||
except Exception:
|
||||
logger.exception('Got un-handled exception from %s', name)
|
||||
|
||||
|
||||
# TODO: move out of this utility class
|
||||
class RootCommand(Command):
|
||||
|
||||
@ -328,22 +346,14 @@ class RootCommand(Command):
|
||||
|
||||
def start_mixer(self, config, mixer_class):
|
||||
logger.info('Starting Mopidy mixer: %s', mixer_class.__name__)
|
||||
mixer = None
|
||||
|
||||
try:
|
||||
with _actor_error_handling(mixer_class.__name__):
|
||||
mixer = mixer_class.start(config=config).proxy()
|
||||
mixer.ping().get()
|
||||
except exceptions.MixerError as exc:
|
||||
logger.error('Mixer (%s) initialization error: %s',
|
||||
mixer_class.__name__, exc.message)
|
||||
except pykka.ActorDeadError as exc:
|
||||
mixer = None
|
||||
logger.error('Mixer actor died: %s', exc)
|
||||
except Exception:
|
||||
logger.exception('Mixer (%s) initialization exception:',
|
||||
mixer_class.__name__)
|
||||
|
||||
return mixer
|
||||
try:
|
||||
mixer.ping().get()
|
||||
return mixer
|
||||
except pykka.ActorDeadError as exc:
|
||||
logger.error('Actor died: %s', exc)
|
||||
return None
|
||||
|
||||
def configure_mixer(self, config, mixer):
|
||||
volume = config['audio']['mixer_volume']
|
||||
@ -364,16 +374,19 @@ class RootCommand(Command):
|
||||
|
||||
backends = []
|
||||
for backend_class in backend_classes:
|
||||
try:
|
||||
with _actor_error_handling(backend_class.__name__):
|
||||
with timer.time_logger(backend_class.__name__):
|
||||
backend = backend_class.start(
|
||||
config=config, audio=audio).proxy()
|
||||
backends.append(backend)
|
||||
except exceptions.BackendError as exc:
|
||||
logger.error(
|
||||
'Backend (%s) initialization error: %s',
|
||||
backend_class.__name__, exc.message)
|
||||
raise
|
||||
backends.append(backend)
|
||||
|
||||
# Block until all on_starts have finished, letting them run in parallel
|
||||
for backend in backends[:]:
|
||||
try:
|
||||
backend.ping().get()
|
||||
except pykka.ActorDeadError as exc:
|
||||
backends.remove(backend)
|
||||
logger.error('Actor died: %s', exc)
|
||||
|
||||
return backends
|
||||
|
||||
@ -388,14 +401,9 @@ class RootCommand(Command):
|
||||
', '.join(f.__name__ for f in frontend_classes) or 'none')
|
||||
|
||||
for frontend_class in frontend_classes:
|
||||
try:
|
||||
with _actor_error_handling(frontend_class.__name__):
|
||||
with timer.time_logger(frontend_class.__name__):
|
||||
frontend_class.start(config=config, core=core)
|
||||
except exceptions.FrontendError as exc:
|
||||
logger.error(
|
||||
'Frontend (%s) initialization error: %s',
|
||||
frontend_class.__name__, exc.message)
|
||||
raise
|
||||
|
||||
def stop_frontends(self, frontend_classes):
|
||||
logger.info('Stopping Mopidy frontends')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user