added logic to help provide dependent service ordering
This commit is contained in:
parent
bf47e5fc0d
commit
37ce407460
4 changed files with 159 additions and 3 deletions
|
@ -804,6 +804,8 @@ class CoreEmu(object):
|
||||||
os.umask(0)
|
os.umask(0)
|
||||||
|
|
||||||
# configuration
|
# configuration
|
||||||
|
if not config:
|
||||||
|
config = {}
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
# session management
|
# session management
|
||||||
|
|
|
@ -211,6 +211,73 @@ class CoreServices(object):
|
||||||
self.defaultservices.clear()
|
self.defaultservices.clear()
|
||||||
self.customservices.clear()
|
self.customservices.clear()
|
||||||
|
|
||||||
|
def get_service_startups(self, services):
|
||||||
|
# generate service map and find starting points
|
||||||
|
node_services = {service.name: service for service in services}
|
||||||
|
is_dependency = set()
|
||||||
|
all_services = set()
|
||||||
|
for service in services:
|
||||||
|
all_services.add(service.name)
|
||||||
|
for service_name in service.dependencies:
|
||||||
|
# check service needed is valid
|
||||||
|
if service_name not in node_services:
|
||||||
|
raise ValueError("service(%s) dependency does not exist: %s" % (service.name, service_name))
|
||||||
|
is_dependency.add(service_name)
|
||||||
|
starting_points = all_services - is_dependency
|
||||||
|
|
||||||
|
# cycles means no starting points
|
||||||
|
if not starting_points:
|
||||||
|
raise ValueError("no valid service starting points")
|
||||||
|
|
||||||
|
stack = [iter(starting_points)]
|
||||||
|
|
||||||
|
# information used to traverse dependency graph
|
||||||
|
visited = set()
|
||||||
|
path = []
|
||||||
|
path_set = set()
|
||||||
|
|
||||||
|
# store startup orderings
|
||||||
|
startups = []
|
||||||
|
startup = []
|
||||||
|
|
||||||
|
logger.debug("starting points: %s", starting_points)
|
||||||
|
while stack:
|
||||||
|
for service_name in stack[-1]:
|
||||||
|
service = node_services[service_name]
|
||||||
|
logger.debug("evaluating: %s", service.name)
|
||||||
|
|
||||||
|
# check this is not a cycle
|
||||||
|
if service.name in path_set:
|
||||||
|
raise ValueError("service has a cyclic dependency: %s" % service.name)
|
||||||
|
# check that we have not already visited this node
|
||||||
|
elif service.name not in visited:
|
||||||
|
logger.debug("visiting: %s", service.name)
|
||||||
|
visited.add(service.name)
|
||||||
|
path.append(service.name)
|
||||||
|
path_set.add(service.name)
|
||||||
|
|
||||||
|
# retrieve and set dependencies to the stack
|
||||||
|
stack.append(iter(service.dependencies))
|
||||||
|
startup.append(service)
|
||||||
|
break
|
||||||
|
# for loop completed without a break
|
||||||
|
else:
|
||||||
|
logger.debug("finished a visit: path(%s)", path)
|
||||||
|
if path:
|
||||||
|
path_set.remove(path.pop())
|
||||||
|
|
||||||
|
if not path and startup:
|
||||||
|
startup.reverse()
|
||||||
|
startups.append(startup)
|
||||||
|
startup = []
|
||||||
|
|
||||||
|
stack.pop()
|
||||||
|
|
||||||
|
if visited != all_services:
|
||||||
|
raise ValueError("cycle encountered, services are being skipped")
|
||||||
|
|
||||||
|
return startups
|
||||||
|
|
||||||
def getdefaultservices(self, service_type):
|
def getdefaultservices(self, service_type):
|
||||||
"""
|
"""
|
||||||
Get the list of default services that should be enabled for a
|
Get the list of default services that should be enabled for a
|
||||||
|
@ -645,6 +712,9 @@ class CoreService(object):
|
||||||
# executables that must exist for service to run
|
# executables that must exist for service to run
|
||||||
executables = ()
|
executables = ()
|
||||||
|
|
||||||
|
# sets service requirements that must be started prior to this service starting
|
||||||
|
dependencies = ()
|
||||||
|
|
||||||
# group string allows grouping services together
|
# group string allows grouping services together
|
||||||
group = None
|
group = None
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ class NrlService(CoreService):
|
||||||
|
|
||||||
class MgenSinkService(NrlService):
|
class MgenSinkService(NrlService):
|
||||||
name = "MGEN_Sink"
|
name = "MGEN_Sink"
|
||||||
|
executables = ("mgen",)
|
||||||
configs = ("sink.mgen",)
|
configs = ("sink.mgen",)
|
||||||
startindex = 5
|
startindex = 5
|
||||||
startup = ("mgen input sink.mgen",)
|
startup = ("mgen input sink.mgen",)
|
||||||
|
@ -73,6 +74,7 @@ class NrlNhdp(NrlService):
|
||||||
NeighborHood Discovery Protocol for MANET networks.
|
NeighborHood Discovery Protocol for MANET networks.
|
||||||
"""
|
"""
|
||||||
name = "NHDP"
|
name = "NHDP"
|
||||||
|
executables = ("nrlnhdp",)
|
||||||
startup = ("nrlnhdp",)
|
startup = ("nrlnhdp",)
|
||||||
shutdown = ("killall nrlnhdp",)
|
shutdown = ("killall nrlnhdp",)
|
||||||
validate = ("pidof nrlnhdp",)
|
validate = ("pidof nrlnhdp",)
|
||||||
|
@ -105,6 +107,7 @@ class NrlSmf(NrlService):
|
||||||
Simplified Multicast Forwarding for MANET networks.
|
Simplified Multicast Forwarding for MANET networks.
|
||||||
"""
|
"""
|
||||||
name = "SMF"
|
name = "SMF"
|
||||||
|
executables = ("nrlsmf",)
|
||||||
startup = ("sh startsmf.sh",)
|
startup = ("sh startsmf.sh",)
|
||||||
shutdown = ("killall nrlsmf",)
|
shutdown = ("killall nrlsmf",)
|
||||||
validate = ("pidof nrlsmf",)
|
validate = ("pidof nrlsmf",)
|
||||||
|
@ -156,6 +159,7 @@ class NrlOlsr(NrlService):
|
||||||
Optimized Link State Routing protocol for MANET networks.
|
Optimized Link State Routing protocol for MANET networks.
|
||||||
"""
|
"""
|
||||||
name = "OLSR"
|
name = "OLSR"
|
||||||
|
executables = ("nrlolsrd",)
|
||||||
startup = ("nrlolsrd",)
|
startup = ("nrlolsrd",)
|
||||||
shutdown = ("killall nrlolsrd",)
|
shutdown = ("killall nrlolsrd",)
|
||||||
validate = ("pidof nrlolsrd",)
|
validate = ("pidof nrlolsrd",)
|
||||||
|
@ -189,6 +193,7 @@ class NrlOlsrv2(NrlService):
|
||||||
Optimized Link State Routing protocol version 2 for MANET networks.
|
Optimized Link State Routing protocol version 2 for MANET networks.
|
||||||
"""
|
"""
|
||||||
name = "OLSRv2"
|
name = "OLSRv2"
|
||||||
|
executables = ("nrlolsrv2",)
|
||||||
startup = ("nrlolsrv2",)
|
startup = ("nrlolsrv2",)
|
||||||
shutdown = ("killall nrlolsrv2",)
|
shutdown = ("killall nrlolsrv2",)
|
||||||
validate = ("pidof nrlolsrv2",)
|
validate = ("pidof nrlolsrv2",)
|
||||||
|
@ -223,6 +228,7 @@ class OlsrOrg(NrlService):
|
||||||
Optimized Link State Routing protocol from olsr.org for MANET networks.
|
Optimized Link State Routing protocol from olsr.org for MANET networks.
|
||||||
"""
|
"""
|
||||||
name = "OLSRORG"
|
name = "OLSRORG"
|
||||||
|
executables = ("olsrd",)
|
||||||
configs = ("/etc/olsrd/olsrd.conf",)
|
configs = ("/etc/olsrd/olsrd.conf",)
|
||||||
dirs = ("/etc/olsrd",)
|
dirs = ("/etc/olsrd",)
|
||||||
startup = ("olsrd",)
|
startup = ("olsrd",)
|
||||||
|
@ -572,6 +578,7 @@ class MgenActor(NrlService):
|
||||||
|
|
||||||
# a unique name is required, without spaces
|
# a unique name is required, without spaces
|
||||||
name = "MgenActor"
|
name = "MgenActor"
|
||||||
|
executables = ("mgen",)
|
||||||
# you can create your own group here
|
# you can create your own group here
|
||||||
group = "ProtoSvc"
|
group = "ProtoSvc"
|
||||||
# list of other services this service depends on
|
# list of other services this service depends on
|
||||||
|
@ -616,6 +623,7 @@ class Arouted(NrlService):
|
||||||
Adaptive Routing
|
Adaptive Routing
|
||||||
"""
|
"""
|
||||||
name = "arouted"
|
name = "arouted"
|
||||||
|
executables = ("arouted",)
|
||||||
configs = ("startarouted.sh",)
|
configs = ("startarouted.sh",)
|
||||||
startindex = NrlService.startindex + 10
|
startindex = NrlService.startindex + 10
|
||||||
startup = ("sh startarouted.sh",)
|
startup = ("sh startarouted.sh",)
|
||||||
|
|
|
@ -1,18 +1,94 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from core.service import CoreService
|
||||||
from core.service import ServiceManager
|
from core.service import ServiceManager
|
||||||
|
|
||||||
_PATH = os.path.abspath(os.path.dirname(__file__))
|
_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||||
_SERVICES_PATH = os.path.join(_PATH, "myservices")
|
_SERVICES_PATH = os.path.join(_PATH, "myservices")
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceA(CoreService):
|
||||||
|
name = "A"
|
||||||
|
dependencies = ("B",)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceB(CoreService):
|
||||||
|
name = "B"
|
||||||
|
dependencies = ("C",)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceC(CoreService):
|
||||||
|
name = "C"
|
||||||
|
dependencies = ()
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceD(CoreService):
|
||||||
|
name = "D"
|
||||||
|
dependencies = ("A",)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceE(CoreService):
|
||||||
|
name = "E"
|
||||||
|
dependencies = ("Z",)
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceF(CoreService):
|
||||||
|
name = "F"
|
||||||
|
dependencies = ()
|
||||||
|
|
||||||
|
|
||||||
class TestServices:
|
class TestServices:
|
||||||
def test_import_service(self):
|
def test_service_import(self):
|
||||||
"""
|
"""
|
||||||
Test importing a custom service.
|
Test importing a custom service.
|
||||||
|
|
||||||
:param conftest.Core core: core fixture to test with
|
|
||||||
"""
|
"""
|
||||||
ServiceManager.add_services(_SERVICES_PATH)
|
ServiceManager.add_services(_SERVICES_PATH)
|
||||||
assert ServiceManager.get("MyService")
|
assert ServiceManager.get("MyService")
|
||||||
assert ServiceManager.get("MyService2")
|
assert ServiceManager.get("MyService2")
|
||||||
|
|
||||||
|
def test_services_dependencies(self, session):
|
||||||
|
# given
|
||||||
|
services = [
|
||||||
|
ServiceA(),
|
||||||
|
ServiceB(),
|
||||||
|
ServiceC(),
|
||||||
|
ServiceD(),
|
||||||
|
ServiceF(),
|
||||||
|
]
|
||||||
|
|
||||||
|
# when
|
||||||
|
startups = session.services.get_service_startups(services)
|
||||||
|
|
||||||
|
# then
|
||||||
|
assert len(startups) == 2
|
||||||
|
|
||||||
|
def test_services_dependencies_not_present(self, session):
|
||||||
|
# given
|
||||||
|
services = [
|
||||||
|
ServiceA(),
|
||||||
|
ServiceB(),
|
||||||
|
ServiceC(),
|
||||||
|
ServiceE()
|
||||||
|
]
|
||||||
|
|
||||||
|
# when
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
session.services.get_service_startups(services)
|
||||||
|
|
||||||
|
def test_services_dependencies_cycle(self, session):
|
||||||
|
# given
|
||||||
|
service_c = ServiceC()
|
||||||
|
service_c.dependencies = ("D",)
|
||||||
|
services = [
|
||||||
|
ServiceA(),
|
||||||
|
ServiceB(),
|
||||||
|
service_c,
|
||||||
|
ServiceD(),
|
||||||
|
ServiceF()
|
||||||
|
]
|
||||||
|
|
||||||
|
# when
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
session.services.get_service_startups(services)
|
||||||
|
|
Loading…
Add table
Reference in a new issue