add daemon Component and ComponentManager classes
This commit is contained in:
parent
cab8416596
commit
68b31a09b4
3 changed files with 152 additions and 0 deletions
57
lbrynet/daemon/Component.py
Normal file
57
lbrynet/daemon/Component.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
import logging
|
||||
from twisted.internet import defer
|
||||
from ComponentManager import ComponentManager
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ComponentType(type):
|
||||
def __new__(mcs, name, bases, newattrs):
|
||||
klass = type.__new__(mcs, name, bases, newattrs)
|
||||
if name != "Component":
|
||||
ComponentManager.components.add(klass)
|
||||
return klass
|
||||
|
||||
|
||||
class Component(object):
|
||||
"""
|
||||
lbrynet-daemon component helper
|
||||
|
||||
Inheriting classes will be automatically registered with the ComponentManager and must implement setup and stop
|
||||
methods
|
||||
"""
|
||||
|
||||
__metaclass__ = ComponentType
|
||||
depends_on = []
|
||||
component_name = None
|
||||
running = False
|
||||
|
||||
@classmethod
|
||||
def setup(cls):
|
||||
raise NotImplementedError() # override
|
||||
|
||||
@classmethod
|
||||
def stop(cls):
|
||||
raise NotImplementedError() # override
|
||||
|
||||
@classmethod
|
||||
@defer.inlineCallbacks
|
||||
def _setup(cls):
|
||||
try:
|
||||
result = yield defer.maybeDeferred(cls.setup)
|
||||
cls.running = True
|
||||
defer.returnValue(result)
|
||||
except Exception as err:
|
||||
log.exception("Error setting up %s", cls.component_name or cls.__name__)
|
||||
raise err
|
||||
|
||||
@classmethod
|
||||
@defer.inlineCallbacks
|
||||
def _stop(cls):
|
||||
try:
|
||||
result = yield defer.maybeDeferred(cls.stop)
|
||||
cls.running = False
|
||||
defer.returnValue(result)
|
||||
except Exception as err:
|
||||
log.exception("Error stopping %s", cls.__name__)
|
||||
raise err
|
93
lbrynet/daemon/ComponentManager.py
Normal file
93
lbrynet/daemon/ComponentManager.py
Normal file
|
@ -0,0 +1,93 @@
|
|||
import logging
|
||||
from twisted.internet import defer
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ComponentManager(object):
|
||||
components = set()
|
||||
|
||||
@classmethod
|
||||
def sort_components(cls, reverse=False):
|
||||
"""
|
||||
Sort components by requirements
|
||||
"""
|
||||
steps = []
|
||||
staged = set()
|
||||
components = set(cls.components)
|
||||
|
||||
# components with no requirements
|
||||
step = []
|
||||
for component in set(components):
|
||||
if not component.depends_on:
|
||||
step.append(component)
|
||||
staged.add(component.component_name)
|
||||
components.remove(component)
|
||||
|
||||
if step:
|
||||
steps.append(step)
|
||||
|
||||
while components:
|
||||
step = []
|
||||
to_stage = set()
|
||||
for component in set(components):
|
||||
reqs_met = 0
|
||||
for needed in component.depends_on:
|
||||
if needed in staged:
|
||||
reqs_met += 1
|
||||
if reqs_met == len(component.depends_on):
|
||||
step.append(component)
|
||||
to_stage.add(component.component_name)
|
||||
components.remove(component)
|
||||
if step:
|
||||
staged.update(to_stage)
|
||||
steps.append(step)
|
||||
elif components:
|
||||
raise SyntaxError("components cannot be started: %s" % components)
|
||||
if reverse:
|
||||
steps.reverse()
|
||||
return steps
|
||||
|
||||
@classmethod
|
||||
@defer.inlineCallbacks
|
||||
def setup(cls):
|
||||
"""
|
||||
Start Components in sequence sorted by requirements
|
||||
|
||||
:return: (defer.Deferred)
|
||||
"""
|
||||
stages = cls.sort_components()
|
||||
for stage in stages:
|
||||
yield defer.DeferredList([component._setup() for component in stage])
|
||||
|
||||
@classmethod
|
||||
@defer.inlineCallbacks
|
||||
def stop(cls):
|
||||
"""
|
||||
Stop Components in reversed startup order
|
||||
|
||||
:return: (defer.Deferred)
|
||||
"""
|
||||
stages = cls.sort_components(reverse=True)
|
||||
for stage in stages:
|
||||
yield defer.DeferredList([component._stop() for component in stage])
|
||||
|
||||
@classmethod
|
||||
def all_components_running(cls, *component_names):
|
||||
"""
|
||||
:return: (bool) True if all specified components are running
|
||||
"""
|
||||
c = {component.component_name: component for component in cls.components}
|
||||
for component in component_names:
|
||||
if component not in c:
|
||||
raise NameError("%s is not a known Component" % component)
|
||||
if not c[component].running:
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def get_component(cls, component_name):
|
||||
for component in cls.components:
|
||||
if component.component_name == component_name:
|
||||
return component
|
||||
raise NameError(component_name)
|
|
@ -17,6 +17,7 @@ from lbrynet.core.Error import InvalidAuthenticationToken
|
|||
from lbrynet.core import utils
|
||||
from lbrynet.daemon.auth.util import APIKey, get_auth_message
|
||||
from lbrynet.daemon.auth.client import LBRY_SECRET
|
||||
from lbrynet.daemon.Component import ComponentManager
|
||||
from lbrynet.undecorated import undecorated
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -132,6 +133,7 @@ class JSONRPCServerType(type):
|
|||
|
||||
class AuthorizedBase(object):
|
||||
__metaclass__ = JSONRPCServerType
|
||||
component_manager = ComponentManager
|
||||
|
||||
@staticmethod
|
||||
def deprecated(new_command=None):
|
||||
|
|
Loading…
Reference in a new issue