refactored service methods to be shorter, updated some logging to debug, added some more service tests

This commit is contained in:
Blake J. Harnden 2018-06-22 15:47:02 -07:00
parent 8186f3716c
commit bb533406a6
23 changed files with 226 additions and 112 deletions

View file

@ -5,7 +5,6 @@ socket server request handlers leveraged by core servers.
import Queue import Queue
import SocketServer import SocketServer
import os import os
import pprint
import shlex import shlex
import shutil import shutil
import sys import sys
@ -1526,19 +1525,19 @@ class CoreHandler(SocketServer.BaseRequestHandler):
continue continue
if event_type == EventTypes.STOP.value or event_type == EventTypes.RESTART.value: if event_type == EventTypes.STOP.value or event_type == EventTypes.RESTART.value:
status = self.session.services.stop_node_service(node, service) status = self.session.services.stop_service(node, service)
if status: if status:
fail += "Stop %s," % service.name fail += "Stop %s," % service.name
if event_type == EventTypes.START.value or event_type == EventTypes.RESTART.value: if event_type == EventTypes.START.value or event_type == EventTypes.RESTART.value:
status = self.session.services.node_service_startup(node, service) status = self.session.services.startup_service(node, service)
if status: if status:
fail += "Start %s(%s)," % service.name fail += "Start %s(%s)," % service.name
if event_type == EventTypes.PAUSE.value: if event_type == EventTypes.PAUSE.value:
status = self.session.services.validate_node_service(node, service) status = self.session.services.validate_service(node, service)
if status: if status:
fail += "%s," % service.name fail += "%s," % service.name
if event_type == EventTypes.RECONFIGURE.value: if event_type == EventTypes.RECONFIGURE.value:
self.session.services.node_service_reconfigure(node, service) self.session.services.service_reconfigure(node, service)
fail_data = "" fail_data = ""
if len(fail) > 0: if len(fail) > 0:
@ -1679,6 +1678,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
Return API messages that describe the current session. Return API messages that describe the current session.
""" """
# find all nodes and links # find all nodes and links
nodes_data = [] nodes_data = []
links_data = [] links_data = []
with self.session._objects_lock: with self.session._objects_lock:
@ -1692,21 +1692,17 @@ class CoreHandler(SocketServer.BaseRequestHandler):
links_data.append(link_data) links_data.append(link_data)
# send all nodes first, so that they will exist for any links # send all nodes first, so that they will exist for any links
logger.info("sending nodes:")
for node_data in nodes_data: for node_data in nodes_data:
logger.info(pprint.pformat(dict(node_data._asdict())))
self.session.broadcast_node(node_data) self.session.broadcast_node(node_data)
logger.info("sending links:")
for link_data in links_data: for link_data in links_data:
logger.info(pprint.pformat(dict(link_data._asdict())))
self.session.broadcast_link(link_data) self.session.broadcast_link(link_data)
# send mobility model info # send mobility model info
for node_id in self.session.mobility.nodes(): for node_id in self.session.mobility.nodes():
node = self.session.get_object(node_id) node = self.session.get_object(node_id)
for model_class, config in self.session.mobility.get_models(node): for model_class, config in self.session.mobility.get_models(node):
logger.info("mobility config: node(%s) class(%s) values(%s)", node_id, model_class, config) logger.debug("mobility config: node(%s) class(%s) values(%s)", node_id, model_class, config)
config_data = ConfigShim.config_data(0, node_id, ConfigFlags.UPDATE.value, model_class, config) config_data = ConfigShim.config_data(0, node_id, ConfigFlags.UPDATE.value, model_class, config)
self.session.broadcast_config(config_data) self.session.broadcast_config(config_data)
@ -1714,7 +1710,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
for node_id in self.session.emane.nodes(): for node_id in self.session.emane.nodes():
node = self.session.get_object(node_id) node = self.session.get_object(node_id)
for model_class, config in self.session.emane.get_models(node): for model_class, config in self.session.emane.get_models(node):
logger.info("emane config: node(%s) class(%s) values(%s)", node_id, model_class, config) logger.debug("emane config: node(%s) class(%s) values(%s)", node_id, model_class, config)
config_data = ConfigShim.config_data(0, node_id, ConfigFlags.UPDATE.value, model_class, config) config_data = ConfigShim.config_data(0, node_id, ConfigFlags.UPDATE.value, model_class, config)
self.session.broadcast_config(config_data) self.session.broadcast_config(config_data)

View file

@ -515,7 +515,7 @@ class EmuSession(Session):
if self.state == EventTypes.RUNTIME_STATE.value and is_boot_node: if self.state == EventTypes.RUNTIME_STATE.value and is_boot_node:
self.write_objects() self.write_objects()
self.add_remove_control_interface(node=node, remove=False) self.add_remove_control_interface(node=node, remove=False)
self.services.boot_node_services(node) self.services.boot_services(node)
return node return node

View file

@ -50,8 +50,8 @@ class ServiceShim(object):
valmap = [service.dirs, service.configs, start_index, service.startup, valmap = [service.dirs, service.configs, start_index, service.startup,
service.shutdown, service.validate, service.meta, start_time] service.shutdown, service.validate, service.meta, start_time]
if not service.custom: if not service.custom:
valmap[1] = service.getconfigfilenames(node) valmap[1] = service.get_configs(node)
valmap[3] = service.getstartup(node) valmap[3] = service.get_startup(node)
vals = map(lambda a, b: "%s=%s" % (a, str(b)), cls.keys, valmap) vals = map(lambda a, b: "%s=%s" % (a, str(b)), cls.keys, valmap)
return "|".join(vals) return "|".join(vals)
@ -217,7 +217,15 @@ class CoreServices(object):
self.default_services.clear() self.default_services.clear()
self.custom_services.clear() self.custom_services.clear()
def node_boot_paths(self, services): def create_boot_paths(self, services):
"""
Create boot paths for starting up services based on dependencies. All services provided and their dependencies
must exist within this set of services, to be valid.
:param list[CoreService] services: service to create boot paths for
:return: list of boot paths for services
:rtype: list[list[CoreService]]
"""
# generate service map and find starting points # generate service map and find starting points
node_services = {service.name: service for service in services} node_services = {service.name: service for service in services}
is_dependency = set() is_dependency = set()
@ -398,7 +406,7 @@ class CoreServices(object):
return files return files
def boot_node_services(self, node): def boot_services(self, node):
""" """
Start all services on a node. Start all services on a node.
@ -408,9 +416,9 @@ class CoreServices(object):
pool = ThreadPool() pool = ThreadPool()
results = [] results = []
boot_paths = self.node_boot_paths(node.services) boot_paths = self.create_boot_paths(node.services)
for boot_path in boot_paths: for boot_path in boot_paths:
result = pool.apply_async(self._boot_node_paths, (node, boot_path)) result = pool.apply_async(self._start_boot_paths, (node, boot_path))
results.append(result) results.append(result)
pool.close() pool.close()
@ -418,7 +426,7 @@ class CoreServices(object):
for result in results: for result in results:
result.get() result.get()
def _boot_node_paths(self, node, boot_path): def _start_boot_paths(self, node, boot_path):
""" """
Start all service boot paths found, based on dependencies. Start all service boot paths found, based on dependencies.
@ -428,9 +436,9 @@ class CoreServices(object):
""" """
logger.debug("booting node service dependencies: %s", boot_path) logger.debug("booting node service dependencies: %s", boot_path)
for service in boot_path: for service in boot_path:
self.boot_node_service(node, service) self.boot_service(node, service)
def boot_node_service(self, node, service): def boot_service(self, node, service):
""" """
Start a service on a node. Create private dirs, generate config Start a service on a node. Create private dirs, generate config
files, and execute startup commands. files, and execute startup commands.
@ -446,11 +454,11 @@ class CoreServices(object):
node.privatedir(directory) node.privatedir(directory)
# create service files # create service files
self.node_service_files(node, service) self.create_service_files(node, service)
# run startup # run startup
wait = service.validation_mode == ServiceMode.BLOCKING wait = service.validation_mode == ServiceMode.BLOCKING
status = self.node_service_startup(node, service, wait) status = self.startup_service(node, service, wait)
if status: if status:
raise ServiceBootError("node(%s) service(%s) error during startup" % (node.name, service.name)) raise ServiceBootError("node(%s) service(%s) error during startup" % (node.name, service.name))
@ -461,7 +469,7 @@ class CoreServices(object):
# run validation commands, if present and not timer mode # run validation commands, if present and not timer mode
if service.validation_mode != ServiceMode.TIMER: if service.validation_mode != ServiceMode.TIMER:
status = self.validate_node_service(node, service) status = self.validate_service(node, service)
if status: if status:
raise ServiceBootError("node(%s) service(%s) failed validation" % (node.name, service.name)) raise ServiceBootError("node(%s) service(%s) failed validation" % (node.name, service.name))
@ -486,7 +494,7 @@ class CoreServices(object):
return True return True
return False return False
def validate_node_service(self, node, service): def validate_service(self, node, service):
""" """
Run the validation command(s) for a service. Run the validation command(s) for a service.
@ -498,7 +506,7 @@ class CoreServices(object):
logger.info("validating node(%s) service(%s)", node.name, service.name) logger.info("validating node(%s) service(%s)", node.name, service.name)
cmds = service.validate cmds = service.validate
if not service.custom: if not service.custom:
cmds = service.getvalidate(node) cmds = service.get_validate(node)
status = 0 status = 0
for cmd in cmds: for cmd in cmds:
@ -511,7 +519,7 @@ class CoreServices(object):
return status return status
def stop_node_services(self, node): def stop_services(self, node):
""" """
Stop all services on a node. Stop all services on a node.
@ -519,9 +527,9 @@ class CoreServices(object):
:return: nothing :return: nothing
""" """
for service in node.services: for service in node.services:
self.stop_node_service(node, service) self.stop_service(node, service)
def stop_node_service(self, node, service): def stop_service(self, node, service):
""" """
Stop a service on a node. Stop a service on a node.
@ -558,7 +566,7 @@ class CoreServices(object):
if service.custom: if service.custom:
config_files = service.configs config_files = service.configs
else: else:
config_files = service.getconfigfilenames(node) config_files = service.get_configs(node)
if filename not in config_files: if filename not in config_files:
raise ValueError("unknown service(%s) config file: %s", service_name, filename) raise ValueError("unknown service(%s) config file: %s", service_name, filename)
@ -566,7 +574,7 @@ class CoreServices(object):
# get the file data # get the file data
data = service.config_data.get(filename) data = service.config_data.get(filename)
if data is None: if data is None:
data = "%s" % service.generateconfig(node, filename) data = "%s" % service.generate_config(node, filename)
else: else:
data = "%s" % data data = "%s" % data
@ -579,7 +587,7 @@ class CoreServices(object):
data=data data=data
) )
def set_service_file(self, node_id, service_name, filename, data): def set_service_file(self, node_id, service_name, file_name, data):
""" """
Receive a File Message from the GUI and store the customized file Receive a File Message from the GUI and store the customized file
in the service config. The filename must match one from the list of in the service config. The filename must match one from the list of
@ -587,7 +595,7 @@ class CoreServices(object):
:param int node_id: node id to set service file :param int node_id: node id to set service file
:param str service_name: service name to set file for :param str service_name: service name to set file for
:param str filename: file name to set :param str file_name: file name to set
:param data: data for file to set :param data: data for file to set
:return: nothing :return: nothing
""" """
@ -597,19 +605,19 @@ class CoreServices(object):
# retrieve custom service # retrieve custom service
service = self.get_service(node_id, service_name) service = self.get_service(node_id, service_name)
if service is None: if service is None:
logger.warn("received filename for unknown service: %s", service_name) logger.warn("received file name for unknown service: %s", service_name)
return return
# validate file being set is valid # validate file being set is valid
cfgfiles = service.configs config_files = service.configs
if filename not in cfgfiles: if file_name not in config_files:
logger.warn("received unknown file '%s' for service '%s'", filename, service_name) logger.warn("received unknown file(%s) for service(%s)", file_name, service_name)
return return
# set custom service file data # set custom service file data
service.config_data[filename] = data service.config_data[file_name] = data
def node_service_startup(self, node, service, wait=False): def startup_service(self, node, service, wait=False):
""" """
Startup a node service. Startup a node service.
@ -622,7 +630,7 @@ class CoreServices(object):
cmds = service.startup cmds = service.startup
if not service.custom: if not service.custom:
cmds = service.getstartup(node) cmds = service.get_startup(node)
status = 0 status = 0
for cmd in cmds: for cmd in cmds:
@ -636,7 +644,7 @@ class CoreServices(object):
status = -1 status = -1
return status return status
def node_service_files(self, node, service): def create_service_files(self, node, service):
""" """
Creates node service files. Creates node service files.
@ -646,16 +654,16 @@ class CoreServices(object):
""" """
logger.info("node(%s) service(%s) creating config files", node.name, service.name) logger.info("node(%s) service(%s) creating config files", node.name, service.name)
# get values depending on if custom or not # get values depending on if custom or not
file_names = service.configs config_files = service.configs
if not service.custom: if not service.custom:
file_names = service.getconfigfilenames(node) config_files = service.get_configs(node)
for file_name in file_names: for file_name in config_files:
logger.debug("generating service config: %s", file_name) logger.debug("generating service config: %s", file_name)
if service.custom: if service.custom:
cfg = service.config_data.get(file_name) cfg = service.config_data.get(file_name)
if cfg is None: if cfg is None:
cfg = service.generateconfig(node, file_name) cfg = service.generate_config(node, file_name)
# cfg may have a file:/// url for copying from a file # cfg may have a file:/// url for copying from a file
try: try:
@ -665,11 +673,11 @@ class CoreServices(object):
logger.exception("error copying service file: %s", file_name) logger.exception("error copying service file: %s", file_name)
continue continue
else: else:
cfg = service.generateconfig(node, file_name) cfg = service.generate_config(node, file_name)
node.nodefile(file_name, cfg) node.nodefile(file_name, cfg)
def node_service_reconfigure(self, node, service): def service_reconfigure(self, node, service):
""" """
Reconfigure a node service. Reconfigure a node service.
@ -677,18 +685,18 @@ class CoreServices(object):
:param CoreService service: service to reconfigure :param CoreService service: service to reconfigure
:return: nothing :return: nothing
""" """
file_names = service.configs config_files = service.configs
if not service.custom: if not service.custom:
file_names = service.getconfigfilenames(node) config_files = service.get_configs(node)
for file_name in file_names: for file_name in config_files:
if file_name[:7] == "file:///": if file_name[:7] == "file:///":
# TODO: implement this # TODO: implement this
raise NotImplementedError raise NotImplementedError
cfg = service.config_data.get(file_name) cfg = service.config_data.get(file_name)
if cfg is None: if cfg is None:
cfg = service.generateconfig(node, file_name) cfg = service.generate_config(node, file_name)
node.nodefile(file_name, cfg) node.nodefile(file_name, cfg)
@ -760,7 +768,7 @@ class CoreService(object):
pass pass
@classmethod @classmethod
def getconfigfilenames(cls, node): def get_configs(cls, node):
""" """
Return the tuple of configuration file filenames. This default method Return the tuple of configuration file filenames. This default method
returns the cls._configs tuple, but this method may be overriden to returns the cls._configs tuple, but this method may be overriden to
@ -773,7 +781,7 @@ class CoreService(object):
return cls.configs return cls.configs
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Generate configuration file given a node object. The filename is Generate configuration file given a node object. The filename is
provided to allow for multiple config files. provided to allow for multiple config files.
@ -787,7 +795,7 @@ class CoreService(object):
raise NotImplementedError raise NotImplementedError
@classmethod @classmethod
def getstartup(cls, node): def get_startup(cls, node):
""" """
Return the tuple of startup commands. This default method Return the tuple of startup commands. This default method
returns the cls.startup tuple, but this method may be returns the cls.startup tuple, but this method may be
@ -801,7 +809,7 @@ class CoreService(object):
return cls.startup return cls.startup
@classmethod @classmethod
def getvalidate(cls, node): def get_validate(cls, node):
""" """
Return the tuple of validate commands. This default method Return the tuple of validate commands. This default method
returns the cls.validate tuple, but this method may be returns the cls.validate tuple, but this method may be

View file

@ -19,7 +19,7 @@ class Bird(CoreService):
validate = ("pidof bird",) validate = ("pidof bird",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Return the bird.conf file contents. Return the bird.conf file contents.
""" """

View file

@ -123,7 +123,7 @@ class DockerService(CoreService):
image = "" image = ""
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Returns a string having contents of a docker.sh script that Returns a string having contents of a docker.sh script that
can be modified to start a specific docker image. can be modified to start a specific docker image.

View file

@ -21,7 +21,7 @@ class NrlService(CoreService):
shutdown = () shutdown = ()
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
return "" return ""
@staticmethod @staticmethod
@ -52,7 +52,7 @@ class MgenSinkService(NrlService):
shutdown = ("killall mgen",) shutdown = ("killall mgen",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
cfg = "0.0 LISTEN UDP 5000\n" cfg = "0.0 LISTEN UDP 5000\n"
for ifc in node.netifs(): for ifc in node.netifs():
name = utils.sysctl_devname(ifc.name) name = utils.sysctl_devname(ifc.name)
@ -60,7 +60,7 @@ class MgenSinkService(NrlService):
return cfg return cfg
@classmethod @classmethod
def getstartup(cls, node): def get_startup(cls, node):
cmd = cls.startup[0] cmd = cls.startup[0]
cmd += " output /tmp/mgen_%s.log" % node.name cmd += " output /tmp/mgen_%s.log" % node.name
return cmd, return cmd,
@ -77,7 +77,7 @@ class NrlNhdp(NrlService):
validate = ("pidof nrlnhdp",) validate = ("pidof nrlnhdp",)
@classmethod @classmethod
def getstartup(cls, node): def get_startup(cls, node):
""" """
Generate the appropriate command-line based on node interfaces. Generate the appropriate command-line based on node interfaces.
""" """
@ -111,7 +111,7 @@ class NrlSmf(NrlService):
configs = ("startsmf.sh",) configs = ("startsmf.sh",)
@classmethod @classmethod
def generateconfig(cls, node, filename, ): def generate_config(cls, node, filename, ):
""" """
Generate a startup script for SMF. Because nrlsmf does not Generate a startup script for SMF. Because nrlsmf does not
daemonize, it can cause problems in some situations when launched daemonize, it can cause problems in some situations when launched
@ -162,7 +162,7 @@ class NrlOlsr(NrlService):
validate = ("pidof nrlolsrd",) validate = ("pidof nrlolsrd",)
@classmethod @classmethod
def getstartup(cls, node): def get_startup(cls, node):
""" """
Generate the appropriate command-line based on node interfaces. Generate the appropriate command-line based on node interfaces.
""" """
@ -196,7 +196,7 @@ class NrlOlsrv2(NrlService):
validate = ("pidof nrlolsrv2",) validate = ("pidof nrlolsrv2",)
@classmethod @classmethod
def getstartup(cls, node): def get_startup(cls, node):
""" """
Generate the appropriate command-line based on node interfaces. Generate the appropriate command-line based on node interfaces.
""" """
@ -233,7 +233,7 @@ class OlsrOrg(NrlService):
validate = ("pidof olsrd",) validate = ("pidof olsrd",)
@classmethod @classmethod
def getstartup(cls, node): def get_startup(cls, node):
""" """
Generate the appropriate command-line based on node interfaces. Generate the appropriate command-line based on node interfaces.
""" """
@ -247,7 +247,7 @@ class OlsrOrg(NrlService):
return cmd, return cmd,
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Generate a default olsrd config file to use the broadcast address of 255.255.255.255. Generate a default olsrd config file to use the broadcast address of 255.255.255.255.
""" """
@ -591,7 +591,7 @@ class MgenActor(NrlService):
shutdown = ("killall mgen",) shutdown = ("killall mgen",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Generate a startup script for MgenActor. Because mgenActor does not Generate a startup script for MgenActor. Because mgenActor does not
daemonize, it can cause problems in some situations when launched daemonize, it can cause problems in some situations when launched
@ -623,7 +623,7 @@ class Arouted(NrlService):
validate = ("pidof arouted",) validate = ("pidof arouted",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Return the Quagga.conf or quaggaboot.sh file contents. Return the Quagga.conf or quaggaboot.sh file contents.
""" """

View file

@ -23,7 +23,7 @@ class Zebra(CoreService):
validate = ("pidof zebra",) validate = ("pidof zebra",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Return the Quagga.conf or quaggaboot.sh file contents. Return the Quagga.conf or quaggaboot.sh file contents.
""" """
@ -259,7 +259,7 @@ class QuaggaService(CoreService):
return False return False
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
return "" return ""
@classmethod @classmethod

View file

@ -14,7 +14,7 @@ class SdnService(CoreService):
group = "SDN" group = "SDN"
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
return "" return ""
@ -28,7 +28,7 @@ class OvsService(SdnService):
shutdown = ('killall ovs-vswitchd', 'killall ovsdb-server') shutdown = ('killall ovs-vswitchd', 'killall ovsdb-server')
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
# Check whether the node is running zebra # Check whether the node is running zebra
has_zebra = 0 has_zebra = 0
for s in node.services: for s in node.services:
@ -101,7 +101,7 @@ class RyuService(SdnService):
shutdown = ('killall ryu-manager',) shutdown = ('killall ryu-manager',)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Return a string that will be written to filename, or sent to the Return a string that will be written to filename, or sent to the
GUI for user customization. GUI for user customization.

View file

@ -18,7 +18,7 @@ class VPNClient(CoreService):
custom_needed = True custom_needed = True
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Return the client.conf and vpnclient.sh file contents to Return the client.conf and vpnclient.sh file contents to
""" """
@ -44,7 +44,7 @@ class VPNServer(CoreService):
custom_needed = True custom_needed = True
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Return the sample server.conf and vpnserver.sh file contents to Return the sample server.conf and vpnserver.sh file contents to
GUI for user customization. GUI for user customization.
@ -70,7 +70,7 @@ class IPsec(CoreService):
custom_needed = True custom_needed = True
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Return the ipsec.conf and racoon.conf file contents to Return the ipsec.conf and racoon.conf file contents to
GUI for user customization. GUI for user customization.
@ -96,7 +96,7 @@ class Firewall(CoreService):
custom_needed = True custom_needed = True
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Return the firewall rule examples to GUI for user customization. Return the firewall rule examples to GUI for user customization.
""" """

View file

@ -18,7 +18,7 @@ class Ucarp(CoreService):
validate = ("pidof ucarp",) validate = ("pidof ucarp",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Return the default file contents Return the default file contents
""" """

View file

@ -24,7 +24,7 @@ class UtilService(CoreService):
shutdown = () shutdown = ()
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
return "" return ""
@ -34,7 +34,7 @@ class IPForwardService(UtilService):
startup = ("sh ipforward.sh",) startup = ("sh ipforward.sh",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
if os.uname()[0] == "Linux": if os.uname()[0] == "Linux":
return cls.generateconfiglinux(node, filename) return cls.generateconfiglinux(node, filename)
else: else:
@ -69,7 +69,7 @@ class DefaultRouteService(UtilService):
startup = ("sh defaultroute.sh",) startup = ("sh defaultroute.sh",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
cfg = "#!/bin/sh\n" cfg = "#!/bin/sh\n"
cfg += "# auto-generated by DefaultRoute service (utility.py)\n" cfg += "# auto-generated by DefaultRoute service (utility.py)\n"
for ifc in node.netifs(): for ifc in node.netifs():
@ -103,7 +103,7 @@ class DefaultMulticastRouteService(UtilService):
startup = ("sh defaultmroute.sh",) startup = ("sh defaultmroute.sh",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
cfg = "#!/bin/sh\n" cfg = "#!/bin/sh\n"
cfg += "# auto-generated by DefaultMulticastRoute service (utility.py)\n" cfg += "# auto-generated by DefaultMulticastRoute service (utility.py)\n"
cfg += "# the first interface is chosen below; please change it " cfg += "# the first interface is chosen below; please change it "
@ -129,7 +129,7 @@ class StaticRouteService(UtilService):
custom_needed = True custom_needed = True
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
cfg = "#!/bin/sh\n" cfg = "#!/bin/sh\n"
cfg += "# auto-generated by StaticRoute service (utility.py)\n#\n" cfg += "# auto-generated by StaticRoute service (utility.py)\n#\n"
cfg += "# NOTE: this service must be customized to be of any use\n" cfg += "# NOTE: this service must be customized to be of any use\n"
@ -170,7 +170,7 @@ class SshService(UtilService):
validate = () validate = ()
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Use a startup script for launching sshd in order to wait for host Use a startup script for launching sshd in order to wait for host
key generation. key generation.
@ -238,7 +238,7 @@ class DhcpService(UtilService):
validate = ("pidof dhcpd",) validate = ("pidof dhcpd",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Generate a dhcpd config file using the network address of Generate a dhcpd config file using the network address of
each interface. each interface.
@ -300,7 +300,7 @@ class DhcpClientService(UtilService):
validate = ("pidof dhclient",) validate = ("pidof dhclient",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Generate a script to invoke dhclient on all interfaces. Generate a script to invoke dhclient on all interfaces.
""" """
@ -332,7 +332,7 @@ class FtpService(UtilService):
validate = ("pidof vsftpd",) validate = ("pidof vsftpd",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Generate a vsftpd.conf configuration file. Generate a vsftpd.conf configuration file.
""" """
@ -368,7 +368,7 @@ class HttpService(UtilService):
APACHEVER22, APACHEVER24 = (22, 24) APACHEVER22, APACHEVER24 = (22, 24)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Generate an apache2.conf configuration file. Generate an apache2.conf configuration file.
""" """
@ -577,7 +577,7 @@ class PcapService(UtilService):
meta = "logs network traffic to pcap packet capture files" meta = "logs network traffic to pcap packet capture files"
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Generate a startpcap.sh traffic logging script. Generate a startpcap.sh traffic logging script.
""" """
@ -615,7 +615,7 @@ class RadvdService(UtilService):
validate = ("pidof radvd",) validate = ("pidof radvd",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Generate a RADVD router advertisement daemon config file Generate a RADVD router advertisement daemon config file
using the network address of each interface. using the network address of each interface.
@ -674,7 +674,7 @@ class AtdService(UtilService):
shutdown = ("pkill atd",) shutdown = ("pkill atd",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
return """ return """
#!/bin/sh #!/bin/sh
echo 00001 > /var/spool/cron/atjobs/.SEQ echo 00001 > /var/spool/cron/atjobs/.SEQ

View file

@ -21,7 +21,7 @@ class XorpRtrmgr(CoreService):
validate = ("pidof xorp_rtrmgr",) validate = ("pidof xorp_rtrmgr",)
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Returns config.boot configuration file text. Other services that Returns config.boot configuration file text. Other services that
depend on this will have generatexorpconfig() hooks that are depend on this will have generatexorpconfig() hooks that are
@ -150,7 +150,7 @@ class XorpService(CoreService):
return "0.0.0.0" return "0.0.0.0"
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
return "" return ""
@classmethod @classmethod

View file

@ -673,7 +673,7 @@ class Session(object):
for obj in self.objects.itervalues(): for obj in self.objects.itervalues():
# TODO: determine if checking for CoreNode alone is ok # TODO: determine if checking for CoreNode alone is ok
if isinstance(obj, nodes.PyCoreNode): if isinstance(obj, nodes.PyCoreNode):
self.services.stop_node_services(obj) self.services.stop_services(obj)
# shutdown emane # shutdown emane
self.emane.shutdown() self.emane.shutdown()
@ -727,7 +727,7 @@ class Session(object):
# add a control interface if configured # add a control interface if configured
logger.info("booting node: %s", obj.name) logger.info("booting node: %s", obj.name)
self.add_remove_control_interface(node=obj, remove=False) self.add_remove_control_interface(node=obj, remove=False)
result = pool.apply_async(self.services.boot_node_services, (obj,)) result = pool.apply_async(self.services.boot_services, (obj,))
results.append(result) results.append(result)
pool.close() pool.close()

View file

@ -370,7 +370,7 @@ class CoreDocumentParser0(object):
filename = file.getAttribute("name") filename = file.getAttribute("name")
files.append(filename) files.append(filename)
data = xmlutils.get_text_child(file) data = xmlutils.get_text_child(file)
self.session.services.set_service_file(node_id=n.objid, service_name=name, filename=filename, data=data) self.session.services.set_service_file(node_id=n.objid, service_name=name, file_name=filename, data=data)
if len(files): if len(files):
values.append("files=%s" % files) values.append("files=%s" % files)

View file

@ -651,7 +651,7 @@ class CoreDocumentParser1(object):
self.session.services.set_service_file( self.session.services.set_service_file(
node_id=node.objid, node_id=node.objid,
service_name=svcname, service_name=svcname,
filename=filename, file_name=filename,
data=data data=data
) )
return str(name) return str(name)

View file

@ -694,7 +694,7 @@ class DeviceElement(NamedXmlElement):
f.setAttribute("name", fn) f.setAttribute("name", fn)
# all file names are added to determine when a file has been deleted # all file names are added to determine when a file has been deleted
s.appendChild(f) s.appendChild(f)
data = svc.configtxt.get(fn) data = svc.config_data.get(fn)
if data is None: if data is None:
# this includes only customized file contents and skips # this includes only customized file contents and skips
# the auto-generated files # the auto-generated files

View file

@ -29,7 +29,7 @@ class MyService(CoreService):
shutdown = () shutdown = ()
@classmethod @classmethod
def generateconfig(cls, node, filename): def generate_config(cls, node, filename):
""" """
Return a string that will be written to filename, or sent to the Return a string that will be written to filename, or sent to the
GUI for user customization. GUI for user customization.

View file

@ -160,7 +160,7 @@ def main():
n.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"]) n.cmd([constants.SYSCTL_BIN, "net.ipv4.icmp_echo_ignore_broadcasts=0"])
if options.services is not None: if options.services is not None:
session.services.add_services(n, "", options.services.split("|")) session.services.add_services(n, "", options.services.split("|"))
session.services.boot_node_services(n) session.services.boot_services(n)
nodelist.append(n) nodelist.append(n)
if i % 25 == 0: if i % 25 == 0:
print "\n%s nodes created " % i, print "\n%s nodes created " % i,

View file

@ -88,7 +88,7 @@ ip forwarding
def boot(self): def boot(self):
self.config() self.config()
self.session.services.boot_node_services(self) self.session.services.boot_services(self)
def bootscript(self): def bootscript(self):
return """\ return """\

View file

@ -421,7 +421,7 @@ class Experiment(object):
tmp.newnetif(self.net, [addr]) tmp.newnetif(self.net, [addr])
self.nodes.append(tmp) self.nodes.append(tmp)
self.session.services.add_services(tmp, "router", "IPForward") self.session.services.add_services(tmp, "router", "IPForward")
self.session.services.boot_node_services(tmp) self.session.services.boot_services(tmp)
self.staticroutes(i, prefix, numnodes) self.staticroutes(i, prefix, numnodes)
# link each node in a chain, with the previous node # link each node in a chain, with the previous node
@ -463,7 +463,7 @@ class Experiment(object):
for i in xrange(1, numnodes + 1): for i in xrange(1, numnodes + 1):
tmp = self.nodes[i - 1] tmp = self.nodes[i - 1]
self.session.services.boot_node_services(tmp) self.session.services.boot_services(tmp)
self.staticroutes(i, prefix, numnodes) self.staticroutes(i, prefix, numnodes)
def setnodes(self): def setnodes(self):

View file

@ -10,10 +10,20 @@ class MyService(CoreService):
group = "Utility" group = "Utility"
configs = ("myservice.sh",) configs = ("myservice.sh",)
startup = ("sh myservice.sh",) startup = ("sh myservice.sh",)
shutdown = ("sh myservice.sh",)
@classmethod
def generate_config(cls, node, filename):
return "# test file"
class MyService2(CoreService): class MyService2(MyService):
name = "MyService2" name = "MyService2"
group = "Utility" group = "Utility"
configs = ("myservice.sh",) configs = ("myservice2.sh",)
startup = ("sh myservice.sh",) startup = ("sh myservice2.sh",)
shutdown = ("sh myservice2.sh",)
@classmethod
def generate_config(cls, node, filename):
return "exit 1"

View file

@ -40,6 +40,91 @@ class ServiceF(CoreService):
class TestServices: class TestServices:
def test_service_file(self, session):
# given
ServiceManager.add_services(_SERVICES_PATH)
my_service = ServiceManager.get("MyService")
node = session.add_node()
file_name = my_service.configs[0]
file_path = node.hostfilename(file_name)
# when
session.services.create_service_files(node, my_service)
# then
assert os.path.exists(file_path)
def test_service_startup(self, session):
# given
ServiceManager.add_services(_SERVICES_PATH)
my_service = ServiceManager.get("MyService")
node = session.add_node()
session.services.create_service_files(node, my_service)
# when
status = session.services.startup_service(node, my_service, wait=True)
# then
assert not status
def test_service_startup_error(self, session):
# given
ServiceManager.add_services(_SERVICES_PATH)
my_service = ServiceManager.get("MyService2")
node = session.add_node()
session.services.create_service_files(node, my_service)
# when
status = session.services.startup_service(node, my_service, wait=True)
# then
assert status
def test_service_stop(self, session):
# given
ServiceManager.add_services(_SERVICES_PATH)
my_service = ServiceManager.get("MyService")
node = session.add_node()
session.services.create_service_files(node, my_service)
# when
status = session.services.stop_service(node, my_service)
# then
assert not status
def test_service_stop_error(self, session):
# given
ServiceManager.add_services(_SERVICES_PATH)
my_service = ServiceManager.get("MyService2")
node = session.add_node()
session.services.create_service_files(node, my_service)
# when
status = session.services.stop_service(node, my_service)
# then
assert status
def test_service_set_file(self, session):
# given
ServiceManager.add_services(_SERVICES_PATH)
my_service = ServiceManager.get("MyService")
node = session.add_node()
file_name = my_service.configs[0]
file_path = node.hostfilename(file_name)
file_data = "# custom file"
session.services.set_service_file(node.objid, my_service.name, file_name, file_data)
custom_service = session.services.get_service(node.objid, my_service.name)
# when
session.services.create_service_files(node, custom_service)
# then
assert os.path.exists(file_path)
with open(file_path, "r") as custom_file:
assert custom_file.read() == file_data
def test_service_import(self): def test_service_import(self):
""" """
Test importing a custom service. Test importing a custom service.
@ -48,8 +133,23 @@ class TestServices:
assert ServiceManager.get("MyService") assert ServiceManager.get("MyService")
assert ServiceManager.get("MyService2") assert ServiceManager.get("MyService2")
def test_service_defaults(self): def test_service_setget(self, session):
pass # given
ServiceManager.add_services(_SERVICES_PATH)
service_name = "MyService"
my_service = ServiceManager.get(service_name)
node = session.add_node()
# when
no_service = session.services.get_service(node.objid, service_name)
default_service = session.services.get_service(node.objid, service_name, default_service=True)
session.services.set_service(node.objid, service_name)
custom_service = session.services.get_service(node.objid, service_name, default_service=True)
# then
assert no_service is None
assert default_service == my_service
assert custom_service and custom_service != my_service
def test_services_dependencies(self, session): def test_services_dependencies(self, session):
# given # given
@ -62,7 +162,7 @@ class TestServices:
] ]
# when # when
startups = session.services.node_boot_paths(services) startups = session.services.create_boot_paths(services)
# then # then
assert len(startups) == 2 assert len(startups) == 2
@ -78,7 +178,7 @@ class TestServices:
# when # when
with pytest.raises(ValueError): with pytest.raises(ValueError):
session.services.node_boot_paths(services) session.services.create_boot_paths(services)
def test_services_dependencies_cycle(self, session): def test_services_dependencies_cycle(self, session):
# given # given
@ -94,4 +194,4 @@ class TestServices:
# when # when
with pytest.raises(ValueError): with pytest.raises(ValueError):
session.services.node_boot_paths(services) session.services.create_boot_paths(services)

View file

@ -61,7 +61,7 @@ def wifisession(opt):
node.newnetif(wifi, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)]) node.newnetif(wifi, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
nodes.append(node) nodes.append(node)
session.services.add_services(node, "router", services_str.split("|")) session.services.add_services(node, "router", services_str.split("|"))
session.services.boot_node_services(node) session.services.boot_services(node)
session.setuprandomwalkmobility(bounds=(1000.0, 750.0, 0)) session.setuprandomwalkmobility(bounds=(1000.0, 750.0, 0))
# PHY tracing # PHY tracing