2018-04-02 21:11:27 +02:00
|
|
|
import logging
|
2019-01-22 23:44:17 +01:00
|
|
|
import asyncio
|
2019-01-21 21:55:50 +01:00
|
|
|
from lbrynet.conf import Config
|
2019-01-22 23:44:17 +01:00
|
|
|
from lbrynet.error import ComponentStartConditionNotMet
|
|
|
|
from lbrynet.dht.peer import PeerManager
|
2018-07-24 18:03:43 +02:00
|
|
|
|
2018-04-02 21:11:27 +02:00
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2018-07-25 07:23:02 +02:00
|
|
|
class RegisteredConditions:
|
2018-07-25 00:35:18 +02:00
|
|
|
conditions = {}
|
|
|
|
|
|
|
|
|
|
|
|
class RequiredConditionType(type):
|
|
|
|
def __new__(mcs, name, bases, newattrs):
|
|
|
|
klass = type.__new__(mcs, name, bases, newattrs)
|
|
|
|
if name != "RequiredCondition":
|
|
|
|
if klass.name in RegisteredConditions.conditions:
|
|
|
|
raise SyntaxError("already have a component registered for \"%s\"" % klass.name)
|
|
|
|
RegisteredConditions.conditions[klass.name] = klass
|
|
|
|
return klass
|
|
|
|
|
|
|
|
|
2018-07-25 07:15:51 +02:00
|
|
|
class RequiredCondition(metaclass=RequiredConditionType):
|
2018-07-25 00:35:18 +02:00
|
|
|
name = ""
|
|
|
|
component = ""
|
|
|
|
message = ""
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def evaluate(component):
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
|
2018-07-25 07:23:02 +02:00
|
|
|
class ComponentManager:
|
2018-07-24 18:03:43 +02:00
|
|
|
default_component_classes = {}
|
|
|
|
|
2019-01-22 23:44:17 +01:00
|
|
|
def __init__(self, conf: Config, analytics_manager=None, skip_components=None,
|
|
|
|
peer_manager=None, **override_components):
|
2019-01-21 21:55:50 +01:00
|
|
|
self.conf = conf
|
2018-07-24 18:03:43 +02:00
|
|
|
self.skip_components = skip_components or []
|
2019-01-22 23:44:17 +01:00
|
|
|
self.loop = asyncio.get_event_loop()
|
|
|
|
self.analytics_manager = analytics_manager
|
2018-07-24 18:03:43 +02:00
|
|
|
self.component_classes = {}
|
|
|
|
self.components = set()
|
2019-01-22 23:44:17 +01:00
|
|
|
self.started = asyncio.Event(loop=self.loop)
|
|
|
|
self.peer_manager = peer_manager or PeerManager(asyncio.get_event_loop_policy().get_event_loop())
|
2018-07-24 18:03:43 +02:00
|
|
|
|
2018-07-06 07:17:20 +02:00
|
|
|
for component_name, component_class in self.default_component_classes.items():
|
2018-07-24 18:03:43 +02:00
|
|
|
if component_name in override_components:
|
|
|
|
component_class = override_components.pop(component_name)
|
|
|
|
if component_name not in self.skip_components:
|
|
|
|
self.component_classes[component_name] = component_class
|
|
|
|
|
|
|
|
if override_components:
|
|
|
|
raise SyntaxError("unexpected components: %s" % override_components)
|
|
|
|
|
2018-07-06 07:17:20 +02:00
|
|
|
for component_class in self.component_classes.values():
|
2018-07-24 18:03:43 +02:00
|
|
|
self.components.add(component_class(self))
|
2018-04-02 21:11:27 +02:00
|
|
|
|
2018-07-25 00:35:18 +02:00
|
|
|
def evaluate_condition(self, condition_name):
|
|
|
|
if condition_name not in RegisteredConditions.conditions:
|
|
|
|
raise NameError(condition_name)
|
|
|
|
condition = RegisteredConditions.conditions[condition_name]
|
|
|
|
try:
|
|
|
|
component = self.get_component(condition.component)
|
2018-10-16 21:04:20 +02:00
|
|
|
result = condition.evaluate(component)
|
2018-11-04 07:24:41 +01:00
|
|
|
except Exception:
|
|
|
|
log.exception('failed to evaluate condition:')
|
2018-07-25 00:35:18 +02:00
|
|
|
result = False
|
2018-10-16 21:04:20 +02:00
|
|
|
return result, "" if result else condition.message
|
2018-07-25 00:35:18 +02:00
|
|
|
|
2018-07-24 18:03:43 +02:00
|
|
|
def sort_components(self, reverse=False):
|
2018-04-02 21:11:27 +02:00
|
|
|
"""
|
|
|
|
Sort components by requirements
|
|
|
|
"""
|
|
|
|
steps = []
|
|
|
|
staged = set()
|
2018-07-24 18:03:43 +02:00
|
|
|
components = set(self.components)
|
2018-04-02 21:11:27 +02:00
|
|
|
|
|
|
|
# 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:
|
2018-07-24 18:03:43 +02:00
|
|
|
step.sort()
|
2018-04-02 21:11:27 +02:00
|
|
|
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:
|
2018-07-24 18:03:43 +02:00
|
|
|
step.sort()
|
2018-04-02 21:11:27 +02:00
|
|
|
staged.update(to_stage)
|
|
|
|
steps.append(step)
|
|
|
|
elif components:
|
2018-07-24 18:03:43 +02:00
|
|
|
raise ComponentStartConditionNotMet("Unresolved dependencies for: %s" % components)
|
2018-04-02 21:11:27 +02:00
|
|
|
if reverse:
|
|
|
|
steps.reverse()
|
|
|
|
return steps
|
|
|
|
|
2018-12-15 21:31:02 +01:00
|
|
|
async def setup(self, **callbacks):
|
|
|
|
""" Start Components in sequence sorted by requirements """
|
2018-07-06 07:17:20 +02:00
|
|
|
for component_name, cb in callbacks.items():
|
2018-07-24 18:03:43 +02:00
|
|
|
if component_name not in self.component_classes:
|
2018-10-30 18:41:38 +01:00
|
|
|
if component_name not in self.skip_components:
|
|
|
|
raise NameError("unknown component: %s" % component_name)
|
2018-04-02 22:49:48 +02:00
|
|
|
if not callable(cb):
|
|
|
|
raise ValueError("%s is not callable" % cb)
|
|
|
|
|
2018-12-15 21:31:02 +01:00
|
|
|
async def _setup(component):
|
|
|
|
await component._setup()
|
2018-04-02 22:49:48 +02:00
|
|
|
if component.component_name in callbacks:
|
2018-12-15 21:31:02 +01:00
|
|
|
maybe_coro = callbacks[component.component_name](component)
|
|
|
|
if asyncio.iscoroutine(maybe_coro):
|
2019-01-22 23:44:17 +01:00
|
|
|
await asyncio.create_task(maybe_coro)
|
2018-04-02 22:49:48 +02:00
|
|
|
|
2018-07-24 18:03:43 +02:00
|
|
|
stages = self.sort_components()
|
2018-04-02 21:11:27 +02:00
|
|
|
for stage in stages:
|
2018-12-15 21:31:02 +01:00
|
|
|
needing_start = [
|
|
|
|
_setup(component) for component in stage if not component.running
|
|
|
|
]
|
|
|
|
if needing_start:
|
|
|
|
await asyncio.wait(needing_start)
|
2019-01-22 23:44:17 +01:00
|
|
|
self.started.set()
|
2018-04-02 21:11:27 +02:00
|
|
|
|
2018-12-15 21:31:02 +01:00
|
|
|
async def stop(self):
|
2018-04-02 21:11:27 +02:00
|
|
|
"""
|
|
|
|
Stop Components in reversed startup order
|
|
|
|
"""
|
2018-07-24 18:03:43 +02:00
|
|
|
stages = self.sort_components(reverse=True)
|
2018-04-02 21:11:27 +02:00
|
|
|
for stage in stages:
|
2018-12-15 21:31:02 +01:00
|
|
|
needing_stop = [
|
|
|
|
component._stop() for component in stage if component.running
|
|
|
|
]
|
|
|
|
if needing_stop:
|
2019-01-22 23:44:17 +01:00
|
|
|
await asyncio.wait(needing_stop, loop=self.loop)
|
2018-04-02 21:11:27 +02:00
|
|
|
|
2018-07-24 18:03:43 +02:00
|
|
|
def all_components_running(self, *component_names):
|
2018-04-02 21:11:27 +02:00
|
|
|
"""
|
2018-04-02 22:49:48 +02:00
|
|
|
Check if components are running
|
|
|
|
|
2018-04-02 21:11:27 +02:00
|
|
|
:return: (bool) True if all specified components are running
|
|
|
|
"""
|
2018-07-24 18:03:43 +02:00
|
|
|
components = {component.component_name: component for component in self.components}
|
2018-04-02 21:11:27 +02:00
|
|
|
for component in component_names:
|
2018-04-02 22:49:48 +02:00
|
|
|
if component not in components:
|
2018-04-02 21:11:27 +02:00
|
|
|
raise NameError("%s is not a known Component" % component)
|
2018-04-02 22:49:48 +02:00
|
|
|
if not components[component].running:
|
2018-04-02 21:11:27 +02:00
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2018-07-24 18:03:43 +02:00
|
|
|
def get_components_status(self):
|
|
|
|
"""
|
|
|
|
List status of all the components, whether they are running or not
|
|
|
|
|
|
|
|
:return: (dict) {(str) component_name: (bool) True is running else False}
|
|
|
|
"""
|
|
|
|
return {
|
|
|
|
component.component_name: component.running
|
|
|
|
for component in self.components
|
|
|
|
}
|
|
|
|
|
|
|
|
def get_component(self, component_name):
|
|
|
|
for component in self.components:
|
2018-04-02 21:11:27 +02:00
|
|
|
if component.component_name == component_name:
|
2018-07-24 18:03:43 +02:00
|
|
|
return component.component
|
2018-04-02 21:11:27 +02:00
|
|
|
raise NameError(component_name)
|