startup: Handle frontend and backend failures

This commit is contained in:
Thomas Adamcik 2015-05-12 21:43:50 +02:00
parent cb4b23f416
commit 399124bf46
2 changed files with 40 additions and 28 deletions

View File

@ -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):

View File

@ -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')