refactored service interaction use names with a _, and cleanup up some of the CoreServices methods

This commit is contained in:
Blake J. Harnden 2018-06-15 14:03:27 -07:00
parent 0bf9c99910
commit e80736061f
27 changed files with 958 additions and 885 deletions

View file

@ -42,8 +42,8 @@ from core.enumerations import SessionTlvs
from core.misc import nodeutils from core.misc import nodeutils
from core.misc import structutils from core.misc import structutils
from core.misc import utils from core.misc import utils
from core.service import CoreService
from core.service import ServiceManager from core.service import ServiceManager
from core.service import ServiceShim
class CoreHandler(SocketServer.BaseRequestHandler): class CoreHandler(SocketServer.BaseRequestHandler):
@ -1072,10 +1072,10 @@ class CoreHandler(SocketServer.BaseRequestHandler):
# sort groups by name and map services to groups # sort groups by name and map services to groups
groups = set() groups = set()
group_map = {} group_map = {}
for service in ServiceManager.services.itervalues(): for service_name in ServiceManager.services.itervalues():
group = service._group group = service_name.group
groups.add(group) groups.add(group)
group_map.setdefault(group, []).append(service) group_map.setdefault(group, []).append(service_name)
groups = sorted(groups, key=lambda x: x.lower()) groups = sorted(groups, key=lambda x: x.lower())
# define tlv values in proper order # define tlv values in proper order
@ -1086,15 +1086,15 @@ class CoreHandler(SocketServer.BaseRequestHandler):
start_index = 1 start_index = 1
logger.info("sorted groups: %s", groups) logger.info("sorted groups: %s", groups)
for group in groups: for group in groups:
services = sorted(group_map[group], key=lambda x: x._name.lower()) services = sorted(group_map[group], key=lambda x: x.name.lower())
logger.info("sorted services for group(%s): %s", group, services) logger.info("sorted services for group(%s): %s", group, services)
end_index = start_index + len(services) - 1 end_index = start_index + len(services) - 1
group_strings.append("%s:%s-%s" % (group, start_index, end_index)) group_strings.append("%s:%s-%s" % (group, start_index, end_index))
start_index = start_index + len(services) start_index += len(services)
for service in services: for service_name in services:
captions.append(service._name) captions.append(service_name.name)
values.append("0") values.append("0")
if service._custom_needed: if service_name.custom_needed:
possible_values.append("1") possible_values.append("1")
else: else:
possible_values.append("") possible_values.append("")
@ -1111,30 +1111,31 @@ class CoreHandler(SocketServer.BaseRequestHandler):
node = self.session.get_object(node_id) node = self.session.get_object(node_id)
if node is None: if node is None:
logger.warn("Request to configure service for unknown node %s", node_id) logger.warn("request to configure service for unknown node %s", node_id)
return replies return replies
servicesstring = opaque.split(':')
services, unknown = self.session.services.servicesfromopaque(opaque, node.objid)
for u in unknown:
logger.warn("Request for unknown service '%s'" % u)
services = ServiceShim.servicesfromopaque(opaque)
if not services: if not services:
return replies return replies
servicesstring = opaque.split(":")
if len(servicesstring) == 3: if len(servicesstring) == 3:
# a file request: e.g. "service:zebra:quagga.conf" # a file request: e.g. "service:zebra:quagga.conf"
file_data = self.session.services.getservicefile(services, node, servicesstring[2]) file_name = servicesstring[2]
service_name = services[0]
file_data = self.session.services.getservicefile(service_name, node, file_name, services)
self.session.broadcast_file(file_data) self.session.broadcast_file(file_data)
# short circuit this request early to avoid returning response below # short circuit this request early to avoid returning response below
return replies return replies
# the first service in the list is the one being configured # the first service in the list is the one being configured
svc = services[0] service_name = services[0]
# send back: # send back:
# dirs, configs, startindex, startup, shutdown, metadata, config # dirs, configs, startindex, startup, shutdown, metadata, config
type_flag = ConfigFlags.UPDATE.value type_flag = ConfigFlags.UPDATE.value
data_types = tuple(repeat(ConfigDataTypes.STRING.value, len(svc.keys))) data_types = tuple(repeat(ConfigDataTypes.STRING.value, len(ServiceShim.keys)))
values = svc.tovaluelist(node, services) service = self.session.services.getcustomservice(node_id, service_name, default_service=True)
values = ServiceShim.tovaluelist(service, node, services)
captions = None captions = None
possible_values = None possible_values = None
groups = None groups = None
@ -1173,15 +1174,21 @@ class CoreHandler(SocketServer.BaseRequestHandler):
self.session.services.defaultservices[key] = values self.session.services.defaultservices[key] = values
logger.debug("default services for type %s set to %s", key, values) logger.debug("default services for type %s set to %s", key, values)
elif node_id: elif node_id:
# store service customized config in self.customservices[] services = ServiceShim.servicesfromopaque(opaque)
services, unknown = self.session.services.servicesfromopaque(opaque, node_id)
for u in unknown:
logger.warn("request for unknown service: %s", u)
if services: if services:
svc = services[0] service_name = services[0]
# set custom service for node
self.session.services.setcustomservice(node_id, service_name)
# set custom values for custom service
service = self.session.services.getcustomservice(node_id, service_name)
if not service:
raise ValueError("custom service(%s) for node(%s) does not exist", service_name, node_id)
values = ConfigShim.str_to_dict(values) values = ConfigShim.str_to_dict(values)
self.session.services.setcustomservice(node_id, svc, values) for name, value in values.iteritems():
ServiceShim.setvalue(service, name, value)
return replies return replies
@ -1324,7 +1331,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
if file_type is not None: if file_type is not None:
if file_type.startswith("service:"): if file_type.startswith("service:"):
_, service_name = file_type.split(':')[:2] _, service_name = file_type.split(':')[:2]
self.session.add_node_service_file(node_num, service_name, file_name, source_name, data) self.session.services.setservicefile(node_num, service_name, file_name, data)
return () return ()
elif file_type.startswith("hook:"): elif file_type.startswith("hook:"):
_, state = file_type.split(':')[:2] _, state = file_type.split(':')[:2]
@ -1430,7 +1437,7 @@ class CoreHandler(SocketServer.BaseRequestHandler):
# TODO: register system for event message handlers, # TODO: register system for event message handlers,
# like confobjs # like confobjs
if name.startswith("service:"): if name.startswith("service:"):
self.session.services_event(event_data) self.handle_service_event(event_data)
handled = True handled = True
elif name.startswith("mobility:"): elif name.startswith("mobility:"):
self.session.mobility_event(event_data) self.session.mobility_event(event_data)
@ -1463,6 +1470,72 @@ class CoreHandler(SocketServer.BaseRequestHandler):
return () return ()
def handle_service_event(self, event_data):
"""
Handle an Event Message used to start, stop, restart, or validate
a service on a given node.
:param EventData event_data: event data to handle
:return: nothing
"""
event_type = event_data.event_type
node_id = event_data.node
name = event_data.name
try:
node = self.session.get_object(node_id)
except KeyError:
logger.warn("ignoring event for service '%s', unknown node '%s'", name, node_id)
return
fail = ""
unknown = []
services = ServiceShim.servicesfromopaque(name)
for service_name in services:
service = self.session.services.getcustomservice(node_id, service_name, default_service=True)
if not service:
unknown.append(service_name)
continue
if event_type == EventTypes.STOP.value or event_type == EventTypes.RESTART.value:
status = self.session.services.stopnodeservice(node, service)
if status != "0":
fail += "Stop %s," % service.name
if event_type == EventTypes.START.value or event_type == EventTypes.RESTART.value:
status = self.session.services.node_service_startup(node, service, services)
if status != "0":
fail += "Start %s(%s)," % service.name
if event_type == EventTypes.PAUSE.value:
status = self.session.services.validatenodeservice(node, service, services)
if status != 0:
fail += "%s," % service.name
if event_type == EventTypes.RECONFIGURE.value:
self.session.services.node_service_reconfigure(node, service, services)
fail_data = ""
if len(fail) > 0:
fail_data += "Fail:" + fail
unknown_data = ""
num = len(unknown)
if num > 0:
for u in unknown:
unknown_data += u
if num > 1:
unknown_data += ", "
num -= 1
logger.warn("Event requested for unknown service(s): %s", unknown_data)
unknown_data = "Unknown:" + unknown_data
event_data = EventData(
node=node_id,
event_type=event_type,
name=name,
data=fail_data + ";" + unknown_data,
time="%s" % time.time()
)
self.session.broadcast_event(event_data)
def handle_session_message(self, message): def handle_session_message(self, message):
""" """
Session Message handler Session Message handler
@ -1620,10 +1693,10 @@ class CoreHandler(SocketServer.BaseRequestHandler):
# service customizations # service customizations
service_configs = self.session.services.getallconfigs() service_configs = self.session.services.getallconfigs()
for node_id, service in service_configs: for node_id, service in service_configs:
opaque = "service:%s" % service._name opaque = "service:%s" % service.name
data_types = tuple(repeat(ConfigDataTypes.STRING.value, len(CoreService.keys))) data_types = tuple(repeat(ConfigDataTypes.STRING.value, len(ServiceShim.keys)))
node = self.session.get_object(node_id) node = self.session.get_object(node_id)
values = CoreService.tovaluelist(node, node.services) values = ServiceShim.tovaluelist(service, node, node.services)
config_data = ConfigData( config_data = ConfigData(
message_type=0, message_type=0,
node=node_id, node=node_id,

View file

@ -218,7 +218,7 @@ class PyCoreObj(object):
if hasattr(self, "services") and len(self.services) != 0: if hasattr(self, "services") and len(self.services) != 0:
nodeservices = [] nodeservices = []
for s in self.services: for s in self.services:
nodeservices.append(s._name) nodeservices.append(s.name)
services = "|".join(nodeservices) services = "|".join(nodeservices)
node_data = NodeData( node_data = NodeData(

View file

@ -507,8 +507,7 @@ class EmuSession(Session):
if _type in [NodeTypes.DEFAULT, NodeTypes.PHYSICAL]: if _type in [NodeTypes.DEFAULT, NodeTypes.PHYSICAL]:
node.type = node_options.model node.type = node_options.model
logger.debug("set node type: %s", node.type) logger.debug("set node type: %s", node.type)
services = "|".join(node_options.services) or None self.services.addservicestonode(node, node.type, node_options.services)
self.services.addservicestonode(node, node.type, services)
# boot nodes if created after runtime, LcxNodes, Physical, and RJ45 are all PyCoreNodes # boot nodes if created after runtime, LcxNodes, Physical, and RJ45 are all PyCoreNodes
is_boot_node = isinstance(node, PyCoreNode) and not nodeutils.is_node(node, NodeTypes.RJ45) is_boot_node = isinstance(node, PyCoreNode) and not nodeutils.is_node(node, NodeTypes.RJ45)
@ -697,21 +696,6 @@ class EmuSession(Session):
state = ":%s" % state state = ":%s" % state
self.set_hook(state, file_name, source_name, data) self.set_hook(state, file_name, source_name, data)
def add_node_service_file(self, node_id, service_name, file_name, source_name, data):
"""
Add a service file for a node.
:param int node_id: node to add service file to
:param str service_name: service file to add
:param str file_name: file name to use
:param str source_name: source file
:param str data: file data to save
:return: nothing
"""
# hack to conform with old logic until updated
service_name = ":%s" % service_name
self.services.setservicefile(node_id, service_name, file_name, source_name, data)
def add_node_file(self, node_id, source_name, file_name, data): def add_node_file(self, node_id, source_name, file_name, data):
""" """
Add a file to a node. Add a file to a node.
@ -749,15 +733,6 @@ class EmuSession(Session):
""" """
self.event_loop.run() self.event_loop.run()
def services_event(self, event_data):
"""
Handle a service event.
:param core.data.EventData event_data: event data to handle
:return:
"""
self.services.handleevent(event_data)
def mobility_event(self, event_data): def mobility_event(self, event_data):
""" """
Handle a mobility event. Handle a mobility event.

View file

@ -6,18 +6,110 @@ The CoreServices class handles configuration messages for sending
a list of available services to the GUI and for configuring individual a list of available services to the GUI and for configuring individual
services. services.
""" """
import time
from core import CoreCommandError from core import CoreCommandError
from core import logger from core import logger
from core.data import EventData
from core.data import FileData from core.data import FileData
from core.enumerations import EventTypes
from core.enumerations import MessageFlags from core.enumerations import MessageFlags
from core.enumerations import RegisterTlvs from core.enumerations import RegisterTlvs
from core.misc import utils from core.misc import utils
class ServiceShim(object):
keys = ["dirs", "files", "startidx", "cmdup", "cmddown", "cmdval", "meta", "starttime"]
@classmethod
def tovaluelist(cls, service, node, services):
"""
Convert service properties into a string list of key=value pairs,
separated by "|".
:param CoreService service: service to get value list for
:param core.netns.nodes.CoreNode node: node to get value list for
:param list[CoreService] services: services for node
:return: value list string
:rtype: str
"""
valmap = [service.dirs, service.configs, service.startindex, service.startup,
service.shutdown, service.validate, service.meta, service.starttime]
if not service.custom:
# this is always reached due to classmethod
valmap[valmap.index(service.configs)] = service.getconfigfilenames(node.objid, services)
valmap[valmap.index(service.startup)] = service.getstartup(node, services)
vals = map(lambda a, b: "%s=%s" % (a, str(b)), cls.keys, valmap)
return "|".join(vals)
@classmethod
def fromvaluelist(cls, service, values):
"""
Convert list of values into properties for this instantiated
(customized) service.
:param CoreService service: service to get value list for
:param dict values: value list to set properties from
:return: nothing
"""
# TODO: support empty value? e.g. override default meta with ''
for key in cls.keys:
try:
cls.setvalue(service, key, values[cls.keys.index(key)])
except IndexError:
# old config does not need to have new keys
logger.exception("error indexing into key")
@classmethod
def setvalue(cls, service, key, value):
"""
Set values for this service.
:param CoreService service: service to get value list for
:param str key: key to set value for
:param value: value of key to set
:return: nothing
"""
if key not in cls.keys:
raise ValueError('key `%s` not in `%s`' % (key, cls.keys))
# this handles data conversion to int, string, and tuples
if value:
if key == "startidx":
value = int(value)
elif key == "meta":
value = str(value)
else:
value = utils.make_tuple_fromstr(value, str)
if key == "dirs":
service.dirs = value
elif key == "files":
service.configs = value
elif key == "startidx":
service.startindex = value
elif key == "cmdup":
service.startup = value
elif key == "cmddown":
service.shutdown = value
elif key == "cmdval":
service.validate = value
elif key == "meta":
service.meta = value
elif key == "starttime":
service.starttime = value
@classmethod
def servicesfromopaque(self, opaque):
"""
Build a list of services from an opaque data string.
:param str opaque: opaque data string
:return: services
:rtype: list
"""
servicesstring = opaque.split(':')
if servicesstring[0] != "service":
return []
return servicesstring[1].split(',')
class ServiceManager(object): class ServiceManager(object):
""" """
Manages services available for CORE nodes to use. Manages services available for CORE nodes to use.
@ -33,7 +125,7 @@ class ServiceManager(object):
:return: nothing :return: nothing
""" """
logger.info("loading service: %s", service.__name__) logger.info("loading service: %s", service.__name__)
name = service._name name = service.name
if name in cls.services: if name in cls.services:
raise ValueError("duplicate service being added: %s" % name) raise ValueError("duplicate service being added: %s" % name)
cls.services[name] = service cls.services[name] = service
@ -45,7 +137,7 @@ class ServiceManager(object):
:param str name: name of the service to retrieve :param str name: name of the service to retrieve
:return: service if it exists, None otherwise :return: service if it exists, None otherwise
:rtype: CoreService :rtype: CoreService.class
""" """
return cls.services.get(name) return cls.services.get(name)
@ -60,7 +152,7 @@ class ServiceManager(object):
""" """
services = utils.load_classes(path, CoreService) services = utils.load_classes(path, CoreService)
for service in services: for service in services:
if not service._name: if not service.name:
continue continue
service.on_load() service.on_load()
cls.add(service) cls.add(service)
@ -76,7 +168,6 @@ class CoreServices(object):
""" """
name = "services" name = "services"
config_type = RegisterTlvs.UTILITY.value config_type = RegisterTlvs.UTILITY.value
_invalid_custom_names = ("core", "api", "emane", "misc", "netns", "phys", "services")
def __init__(self, session): def __init__(self, session):
""" """
@ -84,7 +175,6 @@ class CoreServices(object):
:param core.session.Session session: session this manager is tied to :param core.session.Session session: session this manager is tied to
""" """
# ConfigurableManager.__init__(self)
self.session = session self.session = session
# dict of default services tuples, key is node type # dict of default services tuples, key is node type
self.defaultservices = {} self.defaultservices = {}
@ -109,99 +199,79 @@ class CoreServices(object):
:param service_type: service type to get default services for :param service_type: service type to get default services for
:return: default services :return: default services
:rtype: list :rtype: list[CoreService]
""" """
logger.debug("getting default services for type: %s", service_type) logger.debug("getting default services for type: %s", service_type)
results = [] results = []
if service_type in self.defaultservices: defaults = self.defaultservices.get(service_type, [])
defaults = self.defaultservices[service_type] for name in defaults:
for name in defaults: logger.debug("checking for service with service manager: %s", name)
logger.debug("checking for service with service manager: %s", name) service = ServiceManager.get(name)
service = ServiceManager.get(name) if not service:
if not service: logger.warn("default service %s is unknown", name)
logger.warn("default service %s is unknown", name) else:
else: results.append(service)
results.append(service)
return results return results
def getcustomservice(self, object_id, service): def getcustomservice(self, node_id, service_name, default_service=False):
""" """
Get any custom service configured for the given node that matches the specified service name. Get any custom service configured for the given node that matches the specified service name.
If no custom service is found, return the specified service. If no custom service is found, return the specified service.
:param int object_id: object id to get service from :param int node_id: object id to get service from
:param CoreService service: custom service to retrieve :param str service_name: name of service to retrieve
:param bool default_service: True to return default service when custom does not exist, False returns None
:return: custom service from the node :return: custom service from the node
:rtype: CoreService :rtype: CoreService
""" """
if object_id in self.customservices: node_services = self.customservices.setdefault(node_id, {})
for s in self.customservices[object_id]: default = None
if s._name == service._name: if default_service:
return s default = ServiceManager.get(service_name)
return service return node_services.get(service_name, default)
def setcustomservice(self, object_id, service, config): def setcustomservice(self, node_id, service_name):
""" """
Store service customizations in an instantiated service object Store service customizations in an instantiated service object
using a list of values that came from a config message. using a list of values that came from a config message.
:param int object_id: object id to set custom service for :param int node_id: object id to set custom service for
:param class service: service to set :param str service_name: name of service to set
:param dict config: values to
:return:
"""
logger.debug("setting custom service(%s) for node(%s): %s", object_id, service, config)
if service._custom:
s = service
else:
# instantiate the class, for storing config customization
s = service()
# set custom service configuration
for name, value in config.iteritems():
s.setvalue(name, value)
# assume custom service already in dict
if service._custom:
return
# add the custom service to dict
if object_id in self.customservices:
self.customservices[object_id] += (s,)
else:
self.customservices[object_id] = (s,)
def addservicestonode(self, node, nodetype, services_str):
"""
Populate the node.service list using (1) the list of services
requested from the services TLV, (2) using any custom service
configuration, or (3) using the default services for this node type.
:param core.coreobj.PyCoreNode node: node to add services to
:param str nodetype: node type to add services to
:param str services_str: string formatted service list
:return: nothing :return: nothing
""" """
if services_str is not None: logger.debug("setting custom service(%s) for node: %s", node_id, service_name)
logger.info("setting custom services for node(%s)", node.name) service = self.getcustomservice(node_id, service_name)
services = services_str.split("|") if not service:
for name in services: service_class = ServiceManager.get(service_name)
s = ServiceManager.get(name) service = service_class()
if s is None:
logger.warn("configured service %s for node %s is unknown", name, node.name)
continue
logger.info("adding service to node(%s): %s", node.name, s._name)
s = self.getcustomservice(node.objid, s)
node.addservice(s)
else:
logger.info("setting default services for node(%s) type (%s)", node.name, nodetype)
services = self.getdefaultservices(nodetype)
for s in services:
logger.info("adding service to node(%s): %s", node.name, s._name)
s = self.getcustomservice(node.objid, s)
node.addservice(s)
def getallconfigs(self, use_clsmap=True): # add the custom service to dict
node_services = self.customservices.setdefault(node_id, {})
node_services[service.name] = service
def addservicestonode(self, node, node_type, services=None):
"""
Add services to a node.
:param core.coreobj.PyCoreNode node: node to add services to
:param str node_type: node type to add services to
:param list[str] services: services to add to node
:return: nothing
"""
if not services:
logger.info("using default services for node(%s) type(%s)", node.name, node_type)
services = self.defaultservices.get(node_type, [])
logger.info("setting services for node(%s): %s", node.name, services)
for service_name in services:
service = self.getcustomservice(node.objid, service_name, default_service=True)
if not service:
logger.warn("unknown service(%s) for node(%s)", service_name, node.name)
continue
logger.info("adding service to node(%s): %s", node.name, service_name)
node.addservice(service)
def getallconfigs(self):
""" """
Return (nodenum, service) tuples for all stored configs. Used when reconnecting to a Return (nodenum, service) tuples for all stored configs. Used when reconnecting to a
session or opening XML. session or opening XML.
@ -211,9 +281,9 @@ class CoreServices(object):
:rtype: list :rtype: list
""" """
configs = [] configs = []
for nodenum in self.customservices: for node_id in self.customservices.iterkeys():
for service in self.customservices[nodenum]: for service in self.customservices[node_id].itervalues():
configs.append((nodenum, service)) configs.append((node_id, service))
return configs return configs
def getallfiles(self, service): def getallfiles(self, service):
@ -226,11 +296,11 @@ class CoreServices(object):
""" """
files = [] files = []
if not service._custom: if not service.custom:
return files return files
for filename in service._configs: for filename in service.configs:
data = self.getservicefiledata(service, filename) data = service.configtxt.get(filename)
if data is None: if data is None:
continue continue
files.append((filename, data)) files.append((filename, data))
@ -244,19 +314,19 @@ class CoreServices(object):
:param core.netns.vnode.LxcNode node: node to start services on :param core.netns.vnode.LxcNode node: node to start services on
:return: nothing :return: nothing
""" """
services = sorted(node.services, key=lambda service: service._startindex) services = sorted(node.services, key=lambda x: x.startindex)
use_startup_service = any(map(self.is_startup_service, services)) use_startup_service = any(map(self.is_startup_service, services))
for s in services: for service in services:
if len(str(s._starttime)) > 0: if len(str(service.starttime)) > 0:
try: try:
t = float(s._starttime) starttime = float(service.starttime)
if t > 0.0: if starttime > 0.0:
fn = self.bootnodeservice fn = self.bootnodeservice
self.session.event_loop.add_event(t, fn, node, s, services, False) self.session.event_loop.add_event(starttime, fn, node, service, services, False)
continue continue
except ValueError: except ValueError:
logger.exception("error converting start time to float") logger.exception("error converting start time to float")
self.bootnodeservice(node, s, services, use_startup_service) self.bootnodeservice(node, service, services, use_startup_service)
def bootnodeservice(self, node, service, services, use_startup_service): def bootnodeservice(self, node, service, services, use_startup_service):
""" """
@ -269,12 +339,12 @@ class CoreServices(object):
:param bool use_startup_service: flag to use startup services or not :param bool use_startup_service: flag to use startup services or not
:return: nothing :return: nothing
""" """
if service._custom: if service.custom:
self.bootnodecustomservice(node, service, services, use_startup_service) self.bootnodecustomservice(node, service, services, use_startup_service)
return return
logger.info("starting node(%s) service: %s (%s)", node.name, service._name, service._startindex) logger.info("starting node(%s) service: %s (%s)", node.name, service.name, service.startindex)
for directory in service._dirs: for directory in service.dirs:
node.privatedir(directory) node.privatedir(directory)
for filename in service.getconfigfilenames(node.objid, services): for filename in service.getconfigfilenames(node.objid, services):
@ -299,18 +369,17 @@ class CoreServices(object):
:param bool use_startup_service: flag to use startup services or not :param bool use_startup_service: flag to use startup services or not
:return: nothing :return: nothing
""" """
logger.info("starting service(%s) %s (%s)(custom)", service, service._name, service._startindex) logger.info("starting node(%s) service(custom): %s (%s)", node.name, service.name, service.startindex)
for directory in service._dirs: for directory in service.dirs:
node.privatedir(directory) node.privatedir(directory)
logger.info("service configurations: %s", service._configs) logger.info("service configurations: %s", service.configs)
for i, filename in enumerate(service._configs): for filename in service.configs:
logger.info("generating service config: %s", filename) logger.info("generating service config: %s", filename)
if len(filename) == 0: cfg = service.configtxt.get(filename)
continue
cfg = self.getservicefiledata(service, filename)
if cfg is None: if cfg is None:
cfg = service.generateconfig(node, filename, services) cfg = service.generateconfig(node, filename, services)
# cfg may have a file:/// url for copying from a file # cfg may have a file:/// url for copying from a file
try: try:
if self.copyservicefile(node, filename, cfg): if self.copyservicefile(node, filename, cfg):
@ -323,7 +392,7 @@ class CoreServices(object):
if use_startup_service and not self.is_startup_service(service): if use_startup_service and not self.is_startup_service(service):
return return
for args in service._startup: for args in service.startup:
# TODO: this wait=False can be problematic! # TODO: this wait=False can be problematic!
node.cmd(args, wait=False) node.cmd(args, wait=False)
@ -355,9 +424,9 @@ class CoreServices(object):
:param core.netns.vnode.LxcNode node: node to validate services for :param core.netns.vnode.LxcNode node: node to validate services for
:return: nothing :return: nothing
""" """
services = sorted(node.services, key=lambda service: service._startindex) services = sorted(node.services, key=lambda service: service.startindex)
for s in services: for service in services:
self.validatenodeservice(node, s, services) self.validatenodeservice(node, service, services)
def validatenodeservice(self, node, service, services): def validatenodeservice(self, node, service, services):
""" """
@ -369,22 +438,20 @@ class CoreServices(object):
:return: service validation status :return: service validation status
:rtype: int :rtype: int
""" """
logger.info("validating service for node (%s): %s (%s)", node.name, service._name, service._startindex) logger.info("validating service for node (%s): %s (%s)", node.name, service.name, service.startindex)
if service._custom: if service.custom:
validate_cmds = service._validate validate_cmds = service.validate
else: else:
validate_cmds = service.getvalidate(node, services) validate_cmds = service.getvalidate(node, services)
status = 0 status = 0
# has validate commands for args in validate_cmds:
if validate_cmds: logger.info("validating service %s using: %s", service.name, args)
for args in validate_cmds: try:
logger.info("validating service %s using: %s", service._name, args) node.check_cmd(args)
try: except CoreCommandError:
node.check_cmd(args) logger.exception("validate command failed")
except CoreCommandError: status = -1
logger.exception("validate command failed")
status = -1
return status return status
@ -395,9 +462,9 @@ class CoreServices(object):
:param core.netns.nodes.CoreNode node: node to stop services on :param core.netns.nodes.CoreNode node: node to stop services on
:return: nothing :return: nothing
""" """
services = sorted(node.services, key=lambda service: service._startindex) services = sorted(node.services, key=lambda x: x.startindex)
for s in services: for service in services:
self.stopnodeservice(node, s) self.stopnodeservice(node, service)
def stopnodeservice(self, node, service): def stopnodeservice(self, node, service):
""" """
@ -409,68 +476,57 @@ class CoreServices(object):
:rtype: str :rtype: str
""" """
status = "0" status = "0"
if service._shutdown: for args in service.shutdown:
for args in service._shutdown: try:
try: node.check_cmd(args)
node.check_cmd(args) except CoreCommandError:
except CoreCommandError: logger.exception("error running stop command %s", args)
logger.exception("error running stop command %s", args) # TODO: determine if its ok to just return the bad exit status
# TODO: determine if its ok to just return the bad exit status status = "-1"
status = "-1"
return status return status
def servicesfromopaque(self, opaque, object_id): def getservicefile(self, service_name, node, filename, services):
"""
Build a list of services from an opaque data string.
:param str opaque: opaque data string
:param int object_id: object id
:return: services and unknown services lists tuple
:rtype: tuple
"""
services = []
unknown = []
servicesstring = opaque.split(':')
if servicesstring[0] != "service":
return []
servicenames = servicesstring[1].split(',')
for name in servicenames:
s = ServiceManager.get(name)
s = self.getcustomservice(object_id, s)
if s is None:
unknown.append(name)
else:
services.append(s)
return services, unknown
def getservicefile(self, services, node, filename):
""" """
Send a File Message when the GUI has requested a service file. Send a File Message when the GUI has requested a service file.
The file data is either auto-generated or comes from an existing config. The file data is either auto-generated or comes from an existing config.
:param list services: service list :param str service_name: service to get file from
:param core.netns.vnode.LxcNode node: node to get service file from :param core.netns.vnode.LxcNode node: node to get service file from
:param str filename: file name to retrieve :param str filename: file name to retrieve
:param list[str] services: list of services associated with node
:return: file message for node :return: file message for node
""" """
svc = services[0] # get service to get file from
# get the filename and determine the config file index service = self.getcustomservice(node.objid, service_name, default_service=True)
if svc._custom: if not service:
cfgfiles = svc._configs raise ValueError("invalid service: %s", service_name)
# get service for node
node_services = []
for service_name in services:
node_service = self.getcustomservice(node.objid, service_name, default_service=True)
if not node_service:
logger.warn("unknown service: %s", service)
continue
node_services.append(node_service)
# retrieve config files for default/custom service
if service.custom:
config_files = service.configs
else: else:
cfgfiles = svc.getconfigfilenames(node.objid, services) config_files = service.getconfigfilenames(node.objid, node_services)
if filename not in cfgfiles:
logger.warn("Request for unknown file '%s' for service '%s'" % (filename, services[0])) if filename not in config_files:
return None raise ValueError("unknown service(%s) config file: %s", service_name, filename)
# get the file data # get the file data
data = self.getservicefiledata(svc, filename) data = service.configtxt.get(filename)
if data is None: if data is None:
data = "%s" % svc.generateconfig(node, filename, services) data = "%s" % service.generateconfig(node, filename, services)
else: else:
data = "%s" % data data = "%s" % data
filetypestr = "service:%s" % svc._name
filetypestr = "service:%s" % service.name
return FileData( return FileData(
message_type=MessageFlags.ADD.value, message_type=MessageFlags.ADD.value,
node=node.objid, node=node.objid,
@ -479,141 +535,85 @@ class CoreServices(object):
data=data data=data
) )
def getservicefiledata(self, service, filename): def setservicefile(self, node_id, service_name, filename, data):
"""
Get the customized file data associated with a service. Return None
for invalid filenames or missing file data.
:param CoreService service: service to get file data from
:param str filename: file name to get data from
:return: file data
"""
try:
i = service._configs.index(filename)
except ValueError:
return None
if i >= len(service._configtxt) or service._configtxt[i] is None:
return None
return service._configtxt[i]
def setservicefile(self, nodenum, type, filename, srcname, 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
config files in the service. config files in the service.
:param int nodenum: node id to set service file :param int node_id: node id to set service file
:param str type: file type to set :param str service_name: service name to set file for
:param str filename: file name to set :param str filename: file name to set
:param str srcname: source name of file to set
:param data: data for file to set :param data: data for file to set
:return: nothing :return: nothing
""" """
if len(type.split(':')) < 2: # attempt to set custom service, if needed
logger.warn("Received file type did not contain service info.") self.setcustomservice(node_id, service_name)
return
if srcname is not None: # retrieve custom service
raise NotImplementedError svc = self.getcustomservice(node_id, service_name)
svcid, svcname = type.split(':')[:2]
svc = ServiceManager.get(svcname)
svc = self.getcustomservice(nodenum, svc)
if svc is None: if svc is None:
logger.warn("Received filename for unknown service '%s'" % svcname) logger.warn("received filename for unknown service: %s", service_name)
return return
cfgfiles = svc._configs
# validate file being set is valid
cfgfiles = svc.configs
if filename not in cfgfiles: if filename not in cfgfiles:
logger.warn("Received unknown file '%s' for service '%s'" % (filename, svcname)) logger.warn("received unknown file '%s' for service '%s'", filename, service_name)
return return
i = cfgfiles.index(filename)
configtxtlist = list(svc._configtxt)
numitems = len(configtxtlist)
if numitems < i + 1:
# add empty elements to list to support index assignment
for j in range(1, (i + 2) - numitems):
configtxtlist += None,
configtxtlist[i] = data
svc._configtxt = configtxtlist
def handleevent(self, event_data): # set custom service file data
svc.configtxt[filename] = data
def node_service_startup(self, node, service, services):
""" """
Handle an Event Message used to start, stop, restart, or validate Startup a node service.
a service on a given node.
:param EventData event_data: event data to handle :param PyCoreNode node: node to reconfigure service for
:param CoreService service: service to reconfigure
:param list[CoreService] services: node services
:return: status of startup
:rtype: str
"""
if service.custom:
cmds = service.startup
else:
cmds = service.getstartup(node, services)
status = "0"
for args in cmds:
try:
node.check_cmd(args)
except CoreCommandError:
logger.exception("error starting command")
status = "-1"
return status
def node_service_reconfigure(self, node, service, services):
"""
Reconfigure a node service.
:param PyCoreNode node: node to reconfigure service for
:param CoreService service: service to reconfigure
:param list[CoreService] services: node services
:return: nothing :return: nothing
""" """
event_type = event_data.event_type if service.custom:
node_id = event_data.node cfgfiles = service.configs
name = event_data.name else:
cfgfiles = service.getconfigfilenames(node.objid, services)
try: for filename in cfgfiles:
node = self.session.get_object(node_id) if filename[:7] == "file:///":
except KeyError: # TODO: implement this
logger.warn("Ignoring event for service '%s', unknown node '%s'", name, node_id) raise NotImplementedError
return
fail = "" cfg = service.configtxt.get(filename)
services, unknown = self.servicesfromopaque(name, node_id) if cfg is None:
for s in services: cfg = service.generateconfig(node, filename, services)
if event_type == EventTypes.STOP.value or event_type == EventTypes.RESTART.value:
status = self.stopnodeservice(node, s)
if status != "0":
fail += "Stop %s," % s._name
if event_type == EventTypes.START.value or event_type == EventTypes.RESTART.value:
if s._custom:
cmds = s._startup
else:
cmds = s.getstartup(node, services)
if len(cmds) > 0:
for args in cmds:
try:
node.check_cmd(args)
except CoreCommandError:
logger.exception("error starting command")
fail += "Start %s(%s)," % (s._name, args)
if event_type == EventTypes.PAUSE.value:
status = self.validatenodeservice(node, s, services)
if status != 0:
fail += "%s," % s._name
if event_type == EventTypes.RECONFIGURE.value:
if s._custom:
cfgfiles = s._configs
else:
cfgfiles = s.getconfigfilenames(node.objid, services)
if len(cfgfiles) > 0:
for filename in cfgfiles:
if filename[:7] == "file:///":
# TODO: implement this
raise NotImplementedError
cfg = self.getservicefiledata(s, filename)
if cfg is None:
cfg = s.generateconfig(node, filename, services)
node.nodefile(filename, cfg) node.nodefile(filename, cfg)
fail_data = ""
if len(fail) > 0:
fail_data += "Fail:" + fail
unknown_data = ""
num = len(unknown)
if num > 0:
for u in unknown:
unknown_data += u
if num > 1:
unknown_data += ", "
num -= 1
logger.warn("Event requested for unknown service(s): %s", unknown_data)
unknown_data = "Unknown:" + unknown_data
event_data = EventData(
node=node_id,
event_type=event_type,
name=name,
data=fail_data + ";" + unknown_data,
time="%s" % time.time()
)
self.session.broadcast_event(event_data)
class CoreService(object): class CoreService(object):
@ -621,45 +621,42 @@ class CoreService(object):
Parent class used for defining services. Parent class used for defining services.
""" """
# service name should not include spaces # service name should not include spaces
_name = "" name = None
# group string allows grouping services together # group string allows grouping services together
_group = "" group = None
# list name(s) of services that this service depends upon # list name(s) of services that this service depends upon
_depends = () depends = ()
keys = ["dirs", "files", "startidx", "cmdup", "cmddown", "cmdval", "meta", "starttime"]
# private, per-node directories required by this service # private, per-node directories required by this service
_dirs = () dirs = ()
# config files written by this service # config files written by this service
_configs = () configs = ()
# configs = []
# index used to determine start order with other services # index used to determine start order with other services
_startindex = 0 startindex = 0
# time in seconds after runtime to run startup commands # time in seconds after runtime to run startup commands
_starttime = "" starttime = 0
# list of startup commands # list of startup commands
_startup = () startup = ()
# startup = []
# list of shutdown commands # list of shutdown commands
_shutdown = () shutdown = ()
# list of validate commands # list of validate commands
_validate = () validate = ()
# metadata associated with this service # metadata associated with this service
_meta = "" meta = None
# custom configuration text # custom configuration text
_configtxt = () configtxt = {}
_custom = False custom = False
_custom_needed = False custom_needed = False
def __init__(self): def __init__(self):
""" """
@ -667,7 +664,16 @@ class CoreService(object):
against their config. Services are instantiated when a custom against their config. Services are instantiated when a custom
configuration is used to override their default parameters. configuration is used to override their default parameters.
""" """
self._custom = True self.custom = True
self.dirs = self.__class__.dirs
self.configs = self.__class__.configs
self.startindex = self.__class__.startindex
self.startup = self.__class__.startup
self.shutdown = self.__class__.shutdown
self.validate = self.__class__.validate
self.meta = self.__class__.meta
self.starttime = self.__class__.starttime
self.configtxt = self.__class__.configtxt
@classmethod @classmethod
def on_load(cls): def on_load(cls):
@ -685,7 +691,7 @@ class CoreService(object):
:return: class configuration files :return: class configuration files
:rtype: tuple :rtype: tuple
""" """
return cls._configs return cls.configs
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -716,7 +722,7 @@ class CoreService(object):
:return: startup commands :return: startup commands
:rtype: tuple :rtype: tuple
""" """
return cls._startup return cls.startup
@classmethod @classmethod
def getvalidate(cls, node, services): def getvalidate(cls, node, services):
@ -731,76 +737,4 @@ class CoreService(object):
:return: validation commands :return: validation commands
:rtype: tuple :rtype: tuple
""" """
return cls._validate return cls.validate
@classmethod
def tovaluelist(cls, node, services):
"""
Convert service properties into a string list of key=value pairs,
separated by "|".
:param core.netns.nodes.CoreNode node: node to get value list for
:param list services: services for node
:return: value list string
:rtype: str
"""
valmap = [cls._dirs, cls._configs, cls._startindex, cls._startup,
cls._shutdown, cls._validate, cls._meta, cls._starttime]
if not cls._custom:
# this is always reached due to classmethod
valmap[valmap.index(cls._configs)] = cls.getconfigfilenames(node.objid, services)
valmap[valmap.index(cls._startup)] = cls.getstartup(node, services)
vals = map(lambda a, b: "%s=%s" % (a, str(b)), cls.keys, valmap)
return "|".join(vals)
def fromvaluelist(self, values):
"""
Convert list of values into properties for this instantiated
(customized) service.
:param list values: value list to set properties from
:return: nothing
"""
# TODO: support empty value? e.g. override default meta with ''
for key in self.keys:
try:
self.setvalue(key, values[self.keys.index(key)])
except IndexError:
# old config does not need to have new keys
logger.exception("error indexing into key")
def setvalue(self, key, value):
"""
Set values for this service.
:param str key: key to set value for
:param value: value of key to set
:return: nothing
"""
if key not in self.keys:
raise ValueError('key `%s` not in `%s`' % (key, self.keys))
# this handles data conversion to int, string, and tuples
if value:
if key == "startidx":
value = int(value)
elif key == "meta":
value = str(value)
else:
value = utils.make_tuple_fromstr(value, str)
if key == "dirs":
self._dirs = value
elif key == "files":
self._configs = value
elif key == "startidx":
self._startindex = value
elif key == "cmdup":
self._startup = value
elif key == "cmddown":
self._shutdown = value
elif key == "cmdval":
self._validate = value
elif key == "meta":
self._meta = value
elif key == "starttime":
self._starttime = value

View file

@ -9,22 +9,22 @@ class Bird(CoreService):
""" """
Bird router support Bird router support
""" """
_name = "bird" name = "bird"
_group = "BIRD" group = "BIRD"
_depends = () depends = ()
_dirs = ("/etc/bird",) dirs = ("/etc/bird",)
_configs = ("/etc/bird/bird.conf",) configs = ("/etc/bird/bird.conf",)
_startindex = 35 startindex = 35
_startup = ("bird -c %s" % (_configs[0]),) startup = ("bird -c %s" % (configs[0]),)
_shutdown = ("killall bird",) shutdown = ("killall bird",)
_validate = ("pidof bird",) validate = ("pidof bird",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
""" """
Return the bird.conf file contents. Return the bird.conf file contents.
""" """
if filename == cls._configs[0]: if filename == cls.configs[0]:
return cls.generateBirdConf(node, services) return cls.generateBirdConf(node, services)
else: else:
raise ValueError raise ValueError
@ -35,7 +35,7 @@ class Bird(CoreService):
Helper to return the first IPv4 address of a node as its router ID. Helper to return the first IPv4 address of a node as its router ID.
""" """
for ifc in node.netifs(): for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True: if hasattr(ifc, 'control') and ifc.control is True:
continue continue
for a in ifc.addrlist: for a in ifc.addrlist:
if a.find(".") >= 0: if a.find(".") >= 0:
@ -73,11 +73,11 @@ protocol device {
scan time 10; # Scan interfaces every 10 seconds scan time 10; # Scan interfaces every 10 seconds
} }
""" % (cls._name, cls.routerid(node)) """ % (cls.name, cls.routerid(node))
# Generate protocol specific configurations # Generate protocol specific configurations
for s in services: for s in services:
if cls._name not in s._depends: if cls.name not in s.depends:
continue continue
cfg += s.generatebirdconfig(node) cfg += s.generatebirdconfig(node)
@ -90,15 +90,15 @@ class BirdService(CoreService):
common to Bird's routing daemons. common to Bird's routing daemons.
""" """
_name = None name = None
_group = "BIRD" group = "BIRD"
_depends = ("bird",) depends = ("bird",)
_dirs = () dirs = ()
_configs = () configs = ()
_startindex = 40 startindex = 40
_startup = () startup = ()
_shutdown = () shutdown = ()
_meta = "The config file for this service can be found in the bird service." meta = "The config file for this service can be found in the bird service."
@classmethod @classmethod
def generatebirdconfig(cls, node): def generatebirdconfig(cls, node):
@ -106,14 +106,15 @@ class BirdService(CoreService):
@classmethod @classmethod
def generatebirdifcconfig(cls, node): def generatebirdifcconfig(cls, node):
''' Use only bare interfaces descriptions in generated protocol """
Use only bare interfaces descriptions in generated protocol
configurations. This has the slight advantage of being the same configurations. This has the slight advantage of being the same
everywhere. everywhere.
''' """
cfg = "" cfg = ""
for ifc in node.netifs(): for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True: if hasattr(ifc, 'control') and ifc.control is True:
continue continue
cfg += ' interface "%s";\n' % ifc.name cfg += ' interface "%s";\n' % ifc.name
@ -125,8 +126,8 @@ class BirdBgp(BirdService):
BGP BIRD Service (configuration generation) BGP BIRD Service (configuration generation)
""" """
_name = "BIRD_BGP" name = "BIRD_BGP"
_custom_needed = True custom_needed = True
@classmethod @classmethod
def generatebirdconfig(cls, node): def generatebirdconfig(cls, node):
@ -156,7 +157,7 @@ class BirdOspf(BirdService):
OSPF BIRD Service (configuration generation) OSPF BIRD Service (configuration generation)
""" """
_name = "BIRD_OSPFv2" name = "BIRD_OSPFv2"
@classmethod @classmethod
def generatebirdconfig(cls, node): def generatebirdconfig(cls, node):
@ -181,7 +182,7 @@ class BirdRadv(BirdService):
RADV BIRD Service (configuration generation) RADV BIRD Service (configuration generation)
""" """
_name = "BIRD_RADV" name = "BIRD_RADV"
@classmethod @classmethod
def generatebirdconfig(cls, node): def generatebirdconfig(cls, node):
@ -209,7 +210,7 @@ class BirdRip(BirdService):
RIP BIRD Service (configuration generation) RIP BIRD Service (configuration generation)
""" """
_name = "BIRD_RIP" name = "BIRD_RIP"
@classmethod @classmethod
def generatebirdconfig(cls, node): def generatebirdconfig(cls, node):
@ -231,8 +232,8 @@ class BirdStatic(BirdService):
Static Bird Service (configuration generation) Static Bird Service (configuration generation)
""" """
_name = "BIRD_static" name = "BIRD_static"
_custom_needed = True custom_needed = True
@classmethod @classmethod
def generatebirdconfig(cls, node): def generatebirdconfig(cls, node):

View file

@ -112,16 +112,16 @@ class DockerService(CoreService):
This is a service which will allow running docker containers in a CORE This is a service which will allow running docker containers in a CORE
node. node.
""" """
_name = "Docker" name = "Docker"
_group = "Docker" group = "Docker"
_depends = () depends = ()
_dirs = ('/var/lib/docker/containers/', '/run/shm', '/run/resolvconf',) dirs = ('/var/lib/docker/containers/', '/run/shm', '/run/resolvconf',)
_configs = ('docker.sh',) configs = ('docker.sh',)
_startindex = 50 startindex = 50
_startup = ('sh docker.sh',) startup = ('sh docker.sh',)
_shutdown = ('service docker stop',) shutdown = ('service docker stop',)
# Container image to start # Container image to start
_image = "" image = ""
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -139,7 +139,7 @@ class DockerService(CoreService):
# distros may just be docker # distros may just be docker
cfg += 'service docker start\n' cfg += 'service docker start\n'
cfg += "# you could add a command to start a image here eg:\n" cfg += "# you could add a command to start a image here eg:\n"
if not cls._image: if not cls.image:
cfg += "# docker run -d --net host --name coreDock <imagename>\n" cfg += "# docker run -d --net host --name coreDock <imagename>\n"
else: else:
cfg += """\ cfg += """\
@ -150,7 +150,7 @@ until [ $result -eq 0 ]; do
# this is to alleviate contention to docker's SQLite database # this is to alleviate contention to docker's SQLite database
sleep 0.3 sleep 0.3
done done
""" % (cls._image,) """ % (cls.image,)
return cfg return cfg
@classmethod @classmethod

View file

@ -13,14 +13,14 @@ class NrlService(CoreService):
Parent class for NRL services. Defines properties and methods Parent class for NRL services. Defines properties and methods
common to NRL's routing daemons. common to NRL's routing daemons.
""""" """""
_name = None name = None
_group = "ProtoSvc" group = "ProtoSvc"
_depends = () depends = ()
_dirs = () dirs = ()
_configs = () configs = ()
_startindex = 45 startindex = 45
_startup = () startup = ()
_shutdown = () shutdown = ()
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -34,7 +34,7 @@ class NrlService(CoreService):
interface's prefix length, so e.g. '/32' can turn into '/24'. interface's prefix length, so e.g. '/32' can turn into '/24'.
""" """
for ifc in node.netifs(): for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True: if hasattr(ifc, 'control') and ifc.control is True:
continue continue
for a in ifc.addrlist: for a in ifc.addrlist:
if a.find(".") >= 0: if a.find(".") >= 0:
@ -46,12 +46,12 @@ class NrlService(CoreService):
class MgenSinkService(NrlService): class MgenSinkService(NrlService):
_name = "MGEN_Sink" name = "MGEN_Sink"
_configs = ("sink.mgen",) configs = ("sink.mgen",)
_startindex = 5 startindex = 5
_startup = ("mgen input sink.mgen",) startup = ("mgen input sink.mgen",)
_validate = ("pidof mgen",) validate = ("pidof mgen",)
_shutdown = ("killall mgen",) shutdown = ("killall mgen",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -63,7 +63,7 @@ class MgenSinkService(NrlService):
@classmethod @classmethod
def getstartup(cls, node, services): def getstartup(cls, node, services):
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,
@ -72,27 +72,26 @@ class NrlNhdp(NrlService):
""" """
NeighborHood Discovery Protocol for MANET networks. NeighborHood Discovery Protocol for MANET networks.
""" """
_name = "NHDP" name = "NHDP"
_startup = ("nrlnhdp",) startup = ("nrlnhdp",)
_shutdown = ("killall nrlnhdp",) shutdown = ("killall nrlnhdp",)
_validate = ("pidof nrlnhdp",) validate = ("pidof nrlnhdp",)
@classmethod @classmethod
def getstartup(cls, node, services): def getstartup(cls, node, services):
""" """
Generate the appropriate command-line based on node interfaces. Generate the appropriate command-line based on node interfaces.
""" """
cmd = cls._startup[0] cmd = cls.startup[0]
cmd += " -l /var/log/nrlnhdp.log" cmd += " -l /var/log/nrlnhdp.log"
cmd += " -rpipe %s_nhdp" % node.name cmd += " -rpipe %s_nhdp" % node.name
servicenames = map(lambda x: x._name, services) servicenames = map(lambda x: x.name, services)
if "SMF" in servicenames: if "SMF" in servicenames:
cmd += " -flooding ecds" cmd += " -flooding ecds"
cmd += " -smfClient %s_smf" % node.name cmd += " -smfClient %s_smf" % node.name
netifs = filter(lambda x: not getattr(x, 'control', False), \ netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
node.netifs())
if len(netifs) > 0: if len(netifs) > 0:
interfacenames = map(lambda x: x.name, netifs) interfacenames = map(lambda x: x.name, netifs)
cmd += " -i " cmd += " -i "
@ -105,11 +104,11 @@ class NrlSmf(NrlService):
""" """
Simplified Multicast Forwarding for MANET networks. Simplified Multicast Forwarding for MANET networks.
""" """
_name = "SMF" name = "SMF"
_startup = ("sh startsmf.sh",) startup = ("sh startsmf.sh",)
_shutdown = ("killall nrlsmf",) shutdown = ("killall nrlsmf",)
_validate = ("pidof nrlsmf",) validate = ("pidof nrlsmf",)
_configs = ("startsmf.sh",) configs = ("startsmf.sh",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -123,7 +122,7 @@ class NrlSmf(NrlService):
comments = "" comments = ""
cmd = "nrlsmf instance %s_smf" % node.name cmd = "nrlsmf instance %s_smf" % node.name
servicenames = map(lambda x: x._name, services) servicenames = map(lambda x: x.name, services)
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs()) netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
if len(netifs) == 0: if len(netifs) == 0:
return "" return ""
@ -156,17 +155,17 @@ class NrlOlsr(NrlService):
""" """
Optimized Link State Routing protocol for MANET networks. Optimized Link State Routing protocol for MANET networks.
""" """
_name = "OLSR" name = "OLSR"
_startup = ("nrlolsrd",) startup = ("nrlolsrd",)
_shutdown = ("killall nrlolsrd",) shutdown = ("killall nrlolsrd",)
_validate = ("pidof nrlolsrd",) validate = ("pidof nrlolsrd",)
@classmethod @classmethod
def getstartup(cls, node, services): def getstartup(cls, node, services):
""" """
Generate the appropriate command-line based on node interfaces. Generate the appropriate command-line based on node interfaces.
""" """
cmd = cls._startup[0] cmd = cls.startup[0]
# are multiple interfaces supported? No. # are multiple interfaces supported? No.
netifs = list(node.netifs()) netifs = list(node.netifs())
if len(netifs) > 0: if len(netifs) > 0:
@ -175,7 +174,7 @@ class NrlOlsr(NrlService):
cmd += " -l /var/log/nrlolsrd.log" cmd += " -l /var/log/nrlolsrd.log"
cmd += " -rpipe %s_olsr" % node.name cmd += " -rpipe %s_olsr" % node.name
servicenames = map(lambda x: x._name, services) servicenames = map(lambda x: x.name, services)
if "SMF" in servicenames and not "NHDP" in servicenames: if "SMF" in servicenames and not "NHDP" in servicenames:
cmd += " -flooding s-mpr" cmd += " -flooding s-mpr"
cmd += " -smfClient %s_smf" % node.name cmd += " -smfClient %s_smf" % node.name
@ -189,21 +188,21 @@ 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"
_startup = ("nrlolsrv2",) startup = ("nrlolsrv2",)
_shutdown = ("killall nrlolsrv2",) shutdown = ("killall nrlolsrv2",)
_validate = ("pidof nrlolsrv2",) validate = ("pidof nrlolsrv2",)
@classmethod @classmethod
def getstartup(cls, node, services): def getstartup(cls, node, services):
""" """
Generate the appropriate command-line based on node interfaces. Generate the appropriate command-line based on node interfaces.
""" """
cmd = cls._startup[0] cmd = cls.startup[0]
cmd += " -l /var/log/nrlolsrv2.log" cmd += " -l /var/log/nrlolsrv2.log"
cmd += " -rpipe %s_olsrv2" % node.name cmd += " -rpipe %s_olsrv2" % node.name
servicenames = map(lambda x: x._name, services) servicenames = map(lambda x: x.name, services)
if "SMF" in servicenames: if "SMF" in servicenames:
cmd += " -flooding ecds" cmd += " -flooding ecds"
cmd += " -smfClient %s_smf" % node.name cmd += " -smfClient %s_smf" % node.name
@ -223,19 +222,19 @@ 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"
_configs = ("/etc/olsrd/olsrd.conf",) configs = ("/etc/olsrd/olsrd.conf",)
_dirs = ("/etc/olsrd",) dirs = ("/etc/olsrd",)
_startup = ("olsrd",) startup = ("olsrd",)
_shutdown = ("killall olsrd",) shutdown = ("killall olsrd",)
_validate = ("pidof olsrd",) validate = ("pidof olsrd",)
@classmethod @classmethod
def getstartup(cls, node, services): def getstartup(cls, node, services):
""" """
Generate the appropriate command-line based on node interfaces. Generate the appropriate command-line based on node interfaces.
""" """
cmd = cls._startup[0] cmd = cls.startup[0]
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs()) netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
if len(netifs) > 0: if len(netifs) > 0:
interfacenames = map(lambda x: x.name, netifs) interfacenames = map(lambda x: x.name, netifs)
@ -572,24 +571,24 @@ class MgenActor(NrlService):
""" """
# a unique name is required, without spaces # a unique name is required, without spaces
_name = "MgenActor" name = "MgenActor"
# 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
_depends = () depends = ()
# per-node directories # per-node directories
_dirs = () dirs = ()
# generated files (without a full path this file goes in the node's dir, # generated files (without a full path this file goes in the node's dir,
# e.g. /tmp/pycore.12345/n1.conf/) # e.g. /tmp/pycore.12345/n1.conf/)
_configs = ('start_mgen_actor.sh',) configs = ('start_mgen_actor.sh',)
# this controls the starting order vs other enabled services # this controls the starting order vs other enabled services
_startindex = 50 startindex = 50
# list of startup commands, also may be generated during startup # list of startup commands, also may be generated during startup
_startup = ("sh start_mgen_actor.sh",) startup = ("sh start_mgen_actor.sh",)
# list of validation commands # list of validation commands
_validate = ("pidof mgen",) validate = ("pidof mgen",)
# list of shutdown commands # list of shutdown commands
_shutdown = ("killall mgen",) shutdown = ("killall mgen",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -603,7 +602,7 @@ class MgenActor(NrlService):
comments = "" comments = ""
cmd = "mgenBasicActor.py -n %s -a 0.0.0.0" % node.name cmd = "mgenBasicActor.py -n %s -a 0.0.0.0" % node.name
servicenames = map(lambda x: x._name, services) servicenames = map(lambda x: x.name, services)
netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs()) netifs = filter(lambda x: not getattr(x, 'control', False), node.netifs())
if len(netifs) == 0: if len(netifs) == 0:
return "" return ""
@ -616,12 +615,12 @@ class Arouted(NrlService):
""" """
Adaptive Routing Adaptive Routing
""" """
_name = "arouted" name = "arouted"
_configs = ("startarouted.sh",) configs = ("startarouted.sh",)
_startindex = NrlService._startindex + 10 startindex = NrlService.startindex + 10
_startup = ("sh startarouted.sh",) startup = ("sh startarouted.sh",)
_shutdown = ("pkill arouted",) shutdown = ("pkill arouted",)
_validate = ("pidof arouted",) validate = ("pidof arouted",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):

View file

@ -10,33 +10,33 @@ from core.service import CoreService
class Zebra(CoreService): class Zebra(CoreService):
_name = "zebra" name = "zebra"
_group = "Quagga" group = "Quagga"
_dirs = ("/usr/local/etc/quagga", "/var/run/quagga") dirs = ("/usr/local/etc/quagga", "/var/run/quagga")
_configs = ( configs = (
"/usr/local/etc/quagga/Quagga.conf", "/usr/local/etc/quagga/Quagga.conf",
"quaggaboot.sh", "quaggaboot.sh",
"/usr/local/etc/quagga/vtysh.conf" "/usr/local/etc/quagga/vtysh.conf"
) )
_startindex = 35 startindex = 35
_startup = ("sh quaggaboot.sh zebra",) startup = ("sh quaggaboot.sh zebra",)
_shutdown = ("killall zebra",) shutdown = ("killall zebra",)
_validate = ("pidof zebra",) validate = ("pidof zebra",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
""" """
Return the Quagga.conf or quaggaboot.sh file contents. Return the Quagga.conf or quaggaboot.sh file contents.
""" """
if filename == cls._configs[0]: if filename == cls.configs[0]:
return cls.generateQuaggaConf(node, services) return cls.generateQuaggaConf(node, services)
elif filename == cls._configs[1]: elif filename == cls.configs[1]:
return cls.generateQuaggaBoot(node, services) return cls.generateQuaggaBoot(node, services)
elif filename == cls._configs[2]: elif filename == cls.configs[2]:
return cls.generateVtyshConf(node, services) return cls.generateVtyshConf(node, services)
else: else:
raise ValueError("file name (%s) is not a known configuration: %s", raise ValueError("file name (%s) is not a known configuration: %s",
filename, cls._configs) filename, cls.configs)
@classmethod @classmethod
def generateVtyshConf(cls, node, services): def generateVtyshConf(cls, node, services):
@ -67,12 +67,12 @@ class Zebra(CoreService):
want_ipv4 = False want_ipv4 = False
want_ipv6 = False want_ipv6 = False
for s in services: for s in services:
if cls._name not in s._depends: if cls.name not in s.depends:
continue continue
ifccfg = s.generatequaggaifcconfig(node, ifc) ifccfg = s.generatequaggaifcconfig(node, ifc)
if s._ipv4_routing: if s.ipv4_routing:
want_ipv4 = True want_ipv4 = True
if s._ipv6_routing: if s.ipv6_routing:
want_ipv6 = True want_ipv6 = True
cfgv6 += ifccfg cfgv6 += ifccfg
else: else:
@ -93,7 +93,7 @@ class Zebra(CoreService):
cfg += "!\n" cfg += "!\n"
for s in services: for s in services:
if cls._name not in s._depends: if cls.name not in s.depends:
continue continue
cfg += s.generatequaggaconfig(node) cfg += s.generatequaggaconfig(node)
return cfg return cfg
@ -212,7 +212,7 @@ if [ "$1" != "zebra" ]; then
fi fi
confcheck confcheck
bootquagga bootquagga
""" % (cls._configs[0], quagga_sbin_search, quagga_bin_search, constants.QUAGGA_STATE_DIR) """ % (cls.configs[0], quagga_sbin_search, quagga_bin_search, constants.QUAGGA_STATE_DIR)
class QuaggaService(CoreService): class QuaggaService(CoreService):
@ -220,18 +220,18 @@ class QuaggaService(CoreService):
Parent class for Quagga services. Defines properties and methods Parent class for Quagga services. Defines properties and methods
common to Quagga's routing daemons. common to Quagga's routing daemons.
""" """
_name = None name = None
_group = "Quagga" group = "Quagga"
_depends = ("zebra",) depends = ("zebra",)
_dirs = () dirs = ()
_configs = () configs = ()
_startindex = 40 startindex = 40
_startup = () startup = ()
_shutdown = () shutdown = ()
_meta = "The config file for this service can be found in the Zebra service." meta = "The config file for this service can be found in the Zebra service."
_ipv4_routing = False ipv4_routing = False
_ipv6_routing = False ipv6_routing = False
@staticmethod @staticmethod
def routerid(node): def routerid(node):
@ -239,7 +239,7 @@ class QuaggaService(CoreService):
Helper to return the first IPv4 address of a node as its router ID. Helper to return the first IPv4 address of a node as its router ID.
""" """
for ifc in node.netifs(): for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True: if hasattr(ifc, 'control') and ifc.control is True:
continue continue
for a in ifc.addrlist: for a in ifc.addrlist:
if a.find(".") >= 0: if a.find(".") >= 0:
@ -280,11 +280,11 @@ class Ospfv2(QuaggaService):
not build its own configuration file but has hooks for adding to the not build its own configuration file but has hooks for adding to the
unified Quagga.conf file. unified Quagga.conf file.
""" """
_name = "OSPFv2" name = "OSPFv2"
_startup = () startup = ()
_shutdown = ("killall ospfd",) shutdown = ("killall ospfd",)
_validate = ("pidof ospfd",) validate = ("pidof ospfd",)
_ipv4_routing = True ipv4_routing = True
@staticmethod @staticmethod
def mtucheck(ifc): def mtucheck(ifc):
@ -355,12 +355,12 @@ class Ospfv3(QuaggaService):
not build its own configuration file but has hooks for adding to the not build its own configuration file but has hooks for adding to the
unified Quagga.conf file. unified Quagga.conf file.
""" """
_name = "OSPFv3" name = "OSPFv3"
_startup = () startup = ()
_shutdown = ("killall ospf6d",) shutdown = ("killall ospf6d",)
_validate = ("pidof ospf6d",) validate = ("pidof ospf6d",)
_ipv4_routing = True ipv4_routing = True
_ipv6_routing = True ipv6_routing = True
@staticmethod @staticmethod
def minmtu(ifc): def minmtu(ifc):
@ -436,8 +436,8 @@ class Ospfv3mdr(Ospfv3):
configuration file but has hooks for adding to the configuration file but has hooks for adding to the
unified Quagga.conf file. unified Quagga.conf file.
""" """
_name = "OSPFv3MDR" name = "OSPFv3MDR"
_ipv4_routing = True ipv4_routing = True
@classmethod @classmethod
def generatequaggaifcconfig(cls, node, ifc): def generatequaggaifcconfig(cls, node, ifc):
@ -464,13 +464,13 @@ class Bgp(QuaggaService):
Peers must be manually configured, with a full mesh for those Peers must be manually configured, with a full mesh for those
having the same AS number. having the same AS number.
""" """
_name = "BGP" name = "BGP"
_startup = () startup = ()
_shutdown = ("killall bgpd",) shutdown = ("killall bgpd",)
_validate = ("pidof bgpd",) validate = ("pidof bgpd",)
_custom_needed = True custom_needed = True
_ipv4_routing = True ipv4_routing = True
_ipv6_routing = True ipv6_routing = True
@classmethod @classmethod
def generatequaggaconfig(cls, node): def generatequaggaconfig(cls, node):
@ -489,11 +489,11 @@ class Rip(QuaggaService):
""" """
The RIP service provides IPv4 routing for wired networks. The RIP service provides IPv4 routing for wired networks.
""" """
_name = "RIP" name = "RIP"
_startup = () startup = ()
_shutdown = ("killall ripd",) shutdown = ("killall ripd",)
_validate = ("pidof ripd",) validate = ("pidof ripd",)
_ipv4_routing = True ipv4_routing = True
@classmethod @classmethod
def generatequaggaconfig(cls, node): def generatequaggaconfig(cls, node):
@ -512,11 +512,11 @@ class Ripng(QuaggaService):
""" """
The RIP NG service provides IPv6 routing for wired networks. The RIP NG service provides IPv6 routing for wired networks.
""" """
_name = "RIPNG" name = "RIPNG"
_startup = () startup = ()
_shutdown = ("killall ripngd",) shutdown = ("killall ripngd",)
_validate = ("pidof ripngd",) validate = ("pidof ripngd",)
_ipv6_routing = True ipv6_routing = True
@classmethod @classmethod
def generatequaggaconfig(cls, node): def generatequaggaconfig(cls, node):
@ -536,11 +536,11 @@ class Babel(QuaggaService):
The Babel service provides a loop-avoiding distance-vector routing The Babel service provides a loop-avoiding distance-vector routing
protocol for IPv6 and IPv4 with fast convergence properties. protocol for IPv6 and IPv4 with fast convergence properties.
""" """
_name = "Babel" name = "Babel"
_startup = () startup = ()
_shutdown = ("killall babeld",) shutdown = ("killall babeld",)
_validate = ("pidof babeld",) validate = ("pidof babeld",)
_ipv6_routing = True ipv6_routing = True
@classmethod @classmethod
def generatequaggaconfig(cls, node): def generatequaggaconfig(cls, node):
@ -565,11 +565,11 @@ class Xpimd(QuaggaService):
""" """
PIM multicast routing based on XORP. PIM multicast routing based on XORP.
""" """
_name = 'Xpimd' name = 'Xpimd'
_startup = () startup = ()
_shutdown = ('killall xpimd',) shutdown = ('killall xpimd',)
_validate = ('pidof xpimd',) validate = ('pidof xpimd',)
_ipv4_routing = True ipv4_routing = True
@classmethod @classmethod
def generatequaggaconfig(cls, node): def generatequaggaconfig(cls, node):

View file

@ -11,14 +11,14 @@ class SdnService(CoreService):
""" """
Parent class for SDN services. Parent class for SDN services.
""" """
_name = None name = None
_group = "SDN" group = "SDN"
_depends = () depends = ()
_dirs = () dirs = ()
_configs = () configs = ()
_startindex = 50 startindex = 50
_startup = () startup = ()
_shutdown = () shutdown = ()
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -26,27 +26,27 @@ class SdnService(CoreService):
class OvsService(SdnService): class OvsService(SdnService):
_name = "OvsService" name = "OvsService"
_group = "SDN" group = "SDN"
_depends = () depends = ()
_dirs = ("/etc/openvswitch", "/var/run/openvswitch", "/var/log/openvswitch") dirs = ("/etc/openvswitch", "/var/run/openvswitch", "/var/log/openvswitch")
_configs = ('OvsService.sh',) configs = ('OvsService.sh',)
_startindex = 50 startindex = 50
_startup = ('sh OvsService.sh',) startup = ('sh OvsService.sh',)
_shutdown = ('killall ovs-vswitchd', 'killall ovsdb-server') shutdown = ('killall ovs-vswitchd', 'killall ovsdb-server')
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
# Check whether the node is running zebra # Check whether the node is running zebra
has_zebra = 0 has_zebra = 0
for s in services: for s in services:
if s._name == "zebra": if s.name == "zebra":
has_zebra = 1 has_zebra = 1
# Check whether the node is running an SDN controller # Check whether the node is running an SDN controller
has_sdn_ctrlr = 0 has_sdn_ctrlr = 0
for s in services: for s in services:
if s._name == "ryuService": if s.name == "ryuService":
has_sdn_ctrlr = 1 has_sdn_ctrlr = 1
cfg = "#!/bin/sh\n" cfg = "#!/bin/sh\n"
@ -100,14 +100,14 @@ class OvsService(SdnService):
class RyuService(SdnService): class RyuService(SdnService):
_name = "ryuService" name = "ryuService"
_group = "SDN" group = "SDN"
_depends = () depends = ()
_dirs = () dirs = ()
_configs = ('ryuService.sh',) configs = ('ryuService.sh',)
_startindex = 50 startindex = 50
_startup = ('sh ryuService.sh',) startup = ('sh ryuService.sh',)
_shutdown = ('killall ryu-manager',) shutdown = ('killall ryu-manager',)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):

View file

@ -9,14 +9,14 @@ from core.service import CoreService
class VPNClient(CoreService): class VPNClient(CoreService):
_name = "VPNClient" name = "VPNClient"
_group = "Security" group = "Security"
_configs = ('vpnclient.sh',) configs = ('vpnclient.sh',)
_startindex = 60 startindex = 60
_startup = ('sh vpnclient.sh',) startup = ('sh vpnclient.sh',)
_shutdown = ("killall openvpn",) shutdown = ("killall openvpn",)
_validate = ("pidof openvpn",) validate = ("pidof openvpn",)
_custom_needed = True custom_needed = True
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -36,14 +36,14 @@ class VPNClient(CoreService):
class VPNServer(CoreService): class VPNServer(CoreService):
_name = "VPNServer" name = "VPNServer"
_group = "Security" group = "Security"
_configs = ('vpnserver.sh',) configs = ('vpnserver.sh',)
_startindex = 50 startindex = 50
_startup = ('sh vpnserver.sh',) startup = ('sh vpnserver.sh',)
_shutdown = ("killall openvpn",) shutdown = ("killall openvpn",)
_validate = ("pidof openvpn",) validate = ("pidof openvpn",)
_custom_needed = True custom_needed = True
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -64,13 +64,13 @@ class VPNServer(CoreService):
class IPsec(CoreService): class IPsec(CoreService):
_name = "IPsec" name = "IPsec"
_group = "Security" group = "Security"
_configs = ('ipsec.sh',) configs = ('ipsec.sh',)
_startindex = 60 startindex = 60
_startup = ('sh ipsec.sh',) startup = ('sh ipsec.sh',)
_shutdown = ("killall racoon",) shutdown = ("killall racoon",)
_custom_needed = True custom_needed = True
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -92,12 +92,12 @@ class IPsec(CoreService):
class Firewall(CoreService): class Firewall(CoreService):
_name = "Firewall" name = "Firewall"
_group = "Security" group = "Security"
_configs = ('firewall.sh',) configs = ('firewall.sh',)
_startindex = 20 startindex = 20
_startup = ('sh firewall.sh',) startup = ('sh firewall.sh',)
_custom_needed = True custom_needed = True
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):

View file

@ -8,15 +8,15 @@ class Startup(CoreService):
""" """
A CORE service to start other services in order, serially A CORE service to start other services in order, serially
""" """
_name = 'startup' name = 'startup'
_group = 'Utility' group = 'Utility'
_depends = () depends = ()
_dirs = () dirs = ()
_configs = ('startup.sh',) configs = ('startup.sh',)
_startindex = maxint startindex = maxint
_startup = ('sh startup.sh',) startup = ('sh startup.sh',)
_shutdown = () shutdown = ()
_validate = () validate = ()
@staticmethod @staticmethod
def is_startup_service(s): def is_startup_service(s):
@ -24,13 +24,13 @@ class Startup(CoreService):
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
if filename != cls._configs[0]: if filename != cls.configs[0]:
return '' return ''
script = '#!/bin/sh\n' \ script = '#!/bin/sh\n' \
'# auto-generated by Startup (startup.py)\n\n' \ '# auto-generated by Startup (startup.py)\n\n' \
'exec > startup.log 2>&1\n\n' 'exec > startup.log 2>&1\n\n'
for s in sorted(services, key=lambda x: x._startindex): for s in sorted(services, key=lambda x: x.startindex):
if cls.is_startup_service(s) or len(str(s._starttime)) > 0: if cls.is_startup_service(s) or len(str(s.starttime)) > 0:
continue continue
start = '\n'.join(s.getstartup(node, services)) start = '\n'.join(s.getstartup(node, services))
if start: if start:

View file

@ -8,29 +8,29 @@ UCARP_ETC = "/usr/local/etc/ucarp"
class Ucarp(CoreService): class Ucarp(CoreService):
_name = "ucarp" name = "ucarp"
_group = "Utility" group = "Utility"
_depends = ( ) depends = ( )
_dirs = (UCARP_ETC,) dirs = (UCARP_ETC,)
_configs = ( configs = (
UCARP_ETC + "/default.sh", UCARP_ETC + "/default-up.sh", UCARP_ETC + "/default-down.sh", "ucarpboot.sh",) UCARP_ETC + "/default.sh", UCARP_ETC + "/default-up.sh", UCARP_ETC + "/default-down.sh", "ucarpboot.sh",)
_startindex = 65 startindex = 65
_startup = ("sh ucarpboot.sh",) startup = ("sh ucarpboot.sh",)
_shutdown = ("killall ucarp",) shutdown = ("killall ucarp",)
_validate = ("pidof ucarp",) validate = ("pidof ucarp",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
""" """
Return the default file contents Return the default file contents
""" """
if filename == cls._configs[0]: if filename == cls.configs[0]:
return cls.generateUcarpConf(node, services) return cls.generateUcarpConf(node, services)
elif filename == cls._configs[1]: elif filename == cls.configs[1]:
return cls.generateVipUp(node, services) return cls.generateVipUp(node, services)
elif filename == cls._configs[2]: elif filename == cls.configs[2]:
return cls.generateVipDown(node, services) return cls.generateVipDown(node, services)
elif filename == cls._configs[3]: elif filename == cls.configs[3]:
return cls.generateUcarpBoot(node, services) return cls.generateUcarpBoot(node, services)
else: else:
raise ValueError raise ValueError

View file

@ -16,14 +16,14 @@ class UtilService(CoreService):
""" """
Parent class for utility services. Parent class for utility services.
""" """
_name = None name = None
_group = "Utility" group = "Utility"
_depends = () depends = ()
_dirs = () dirs = ()
_configs = () configs = ()
_startindex = 80 startindex = 80
_startup = () startup = ()
_shutdown = () shutdown = ()
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -31,10 +31,10 @@ class UtilService(CoreService):
class IPForwardService(UtilService): class IPForwardService(UtilService):
_name = "IPForward" name = "IPForward"
_configs = ("ipforward.sh",) configs = ("ipforward.sh",)
_startindex = 5 startindex = 5
_startup = ("sh ipforward.sh",) startup = ("sh ipforward.sh",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -67,9 +67,9 @@ class IPForwardService(UtilService):
class DefaultRouteService(UtilService): class DefaultRouteService(UtilService):
_name = "DefaultRoute" name = "DefaultRoute"
_configs = ("defaultroute.sh",) configs = ("defaultroute.sh",)
_startup = ("sh defaultroute.sh",) startup = ("sh defaultroute.sh",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -101,9 +101,9 @@ class DefaultRouteService(UtilService):
class DefaultMulticastRouteService(UtilService): class DefaultMulticastRouteService(UtilService):
_name = "DefaultMulticastRoute" name = "DefaultMulticastRoute"
_configs = ("defaultmroute.sh",) configs = ("defaultmroute.sh",)
_startup = ("sh defaultmroute.sh",) startup = ("sh defaultmroute.sh",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -113,7 +113,7 @@ class DefaultMulticastRouteService(UtilService):
cfg += "as needed\n" cfg += "as needed\n"
for ifc in node.netifs(): for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True: if hasattr(ifc, 'control') and ifc.control is True:
continue continue
if os.uname()[0] == "Linux": if os.uname()[0] == "Linux":
rtcmd = "ip route add 224.0.0.0/4 dev" rtcmd = "ip route add 224.0.0.0/4 dev"
@ -126,10 +126,10 @@ class DefaultMulticastRouteService(UtilService):
class StaticRouteService(UtilService): class StaticRouteService(UtilService):
_name = "StaticRoute" name = "StaticRoute"
_configs = ("staticroute.sh",) configs = ("staticroute.sh",)
_startup = ("sh staticroute.sh",) startup = ("sh staticroute.sh",)
_custom_needed = True custom_needed = True
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -165,12 +165,12 @@ class StaticRouteService(UtilService):
class SshService(UtilService): class SshService(UtilService):
_name = "SSH" name = "SSH"
_configs = ("startsshd.sh", "/etc/ssh/sshd_config",) configs = ("startsshd.sh", "/etc/ssh/sshd_config",)
_dirs = ("/etc/ssh", "/var/run/sshd",) dirs = ("/etc/ssh", "/var/run/sshd",)
_startup = ("sh startsshd.sh",) startup = ("sh startsshd.sh",)
_shutdown = ("killall sshd",) shutdown = ("killall sshd",)
_validate = () validate = ()
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -178,8 +178,8 @@ class SshService(UtilService):
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.
""" """
sshcfgdir = cls._dirs[0] sshcfgdir = cls.dirs[0]
sshstatedir = cls._dirs[1] sshstatedir = cls.dirs[1]
sshlibdir = "/usr/lib/openssh" sshlibdir = "/usr/lib/openssh"
if filename == "startsshd.sh": if filename == "startsshd.sh":
return """\ return """\
@ -233,12 +233,12 @@ UseDNS no
class DhcpService(UtilService): class DhcpService(UtilService):
_name = "DHCP" name = "DHCP"
_configs = ("/etc/dhcp/dhcpd.conf",) configs = ("/etc/dhcp/dhcpd.conf",)
_dirs = ("/etc/dhcp",) dirs = ("/etc/dhcp",)
_startup = ("dhcpd",) startup = ("dhcpd",)
_shutdown = ("killall dhcpd",) shutdown = ("killall dhcpd",)
_validate = ("pidof dhcpd",) validate = ("pidof dhcpd",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -296,11 +296,11 @@ class DhcpClientService(UtilService):
""" """
Use a DHCP client for all interfaces for addressing. Use a DHCP client for all interfaces for addressing.
""" """
_name = "DHCPClient" name = "DHCPClient"
_configs = ("startdhcpclient.sh",) configs = ("startdhcpclient.sh",)
_startup = ("sh startdhcpclient.sh",) startup = ("sh startdhcpclient.sh",)
_shutdown = ("killall dhclient",) shutdown = ("killall dhclient",)
_validate = ("pidof dhclient",) validate = ("pidof dhclient",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -327,12 +327,12 @@ class FtpService(UtilService):
""" """
Start a vsftpd server. Start a vsftpd server.
""" """
_name = "FTP" name = "FTP"
_configs = ("vsftpd.conf",) configs = ("vsftpd.conf",)
_dirs = ("/var/run/vsftpd/empty", "/var/ftp",) dirs = ("/var/run/vsftpd/empty", "/var/ftp",)
_startup = ("vsftpd ./vsftpd.conf",) startup = ("vsftpd ./vsftpd.conf",)
_shutdown = ("killall vsftpd",) shutdown = ("killall vsftpd",)
_validate = ("pidof vsftpd",) validate = ("pidof vsftpd",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -359,14 +359,14 @@ class HttpService(UtilService):
""" """
Start an apache server. Start an apache server.
""" """
_name = "HTTP" name = "HTTP"
_configs = ("/etc/apache2/apache2.conf", "/etc/apache2/envvars", configs = ("/etc/apache2/apache2.conf", "/etc/apache2/envvars",
"/var/www/index.html",) "/var/www/index.html",)
_dirs = ("/etc/apache2", "/var/run/apache2", "/var/log/apache2", dirs = ("/etc/apache2", "/var/run/apache2", "/var/log/apache2",
"/run/lock", "/var/lock/apache2", "/var/www",) "/run/lock", "/var/lock/apache2", "/var/www",)
_startup = ("chown www-data /var/lock/apache2", "apache2ctl start",) startup = ("chown www-data /var/lock/apache2", "apache2ctl start",)
_shutdown = ("apache2ctl stop",) shutdown = ("apache2ctl stop",)
_validate = ("pidof apache2",) validate = ("pidof apache2",)
APACHEVER22, APACHEVER24 = (22, 24) APACHEVER22, APACHEVER24 = (22, 24)
@ -375,11 +375,11 @@ class HttpService(UtilService):
""" """
Generate an apache2.conf configuration file. Generate an apache2.conf configuration file.
""" """
if filename == cls._configs[0]: if filename == cls.configs[0]:
return cls.generateapache2conf(node, filename, services) return cls.generateapache2conf(node, filename, services)
elif filename == cls._configs[1]: elif filename == cls.configs[1]:
return cls.generateenvvars(node, filename, services) return cls.generateenvvars(node, filename, services)
elif filename == cls._configs[2]: elif filename == cls.configs[2]:
return cls.generatehtml(node, filename, services) return cls.generatehtml(node, filename, services)
else: else:
return "" return ""
@ -561,7 +561,7 @@ export LANG
<p>The web server software is running but no content has been added, yet.</p> <p>The web server software is running but no content has been added, yet.</p>
""" % node.name """ % node.name
for ifc in node.netifs(): for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True: if hasattr(ifc, 'control') and ifc.control is True:
continue continue
body += "<li>%s - %s</li>\n" % (ifc.name, ifc.addrlist) body += "<li>%s - %s</li>\n" % (ifc.name, ifc.addrlist)
return "<html><body>%s</body></html>" % body return "<html><body>%s</body></html>" % body
@ -571,14 +571,14 @@ class PcapService(UtilService):
""" """
Pcap service for logging packets. Pcap service for logging packets.
""" """
_name = "pcap" name = "pcap"
_configs = ("pcap.sh",) configs = ("pcap.sh",)
_dirs = () dirs = ()
_startindex = 1 startindex = 1
_startup = ("sh pcap.sh start",) startup = ("sh pcap.sh start",)
_shutdown = ("sh pcap.sh stop",) shutdown = ("sh pcap.sh stop",)
_validate = ("pidof tcpdump",) validate = ("pidof tcpdump",)
_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, services): def generateconfig(cls, node, filename, services):
@ -595,7 +595,7 @@ if [ "x$1" = "xstart" ]; then
""" """
for ifc in node.netifs(): for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True: if hasattr(ifc, 'control') and ifc.control is True:
cfg += '# ' cfg += '# '
redir = "< /dev/null" redir = "< /dev/null"
cfg += "tcpdump ${DUMPOPTS} -w %s.%s.pcap -i %s %s &\n" % \ cfg += "tcpdump ${DUMPOPTS} -w %s.%s.pcap -i %s %s &\n" % \
@ -611,12 +611,12 @@ fi;
class RadvdService(UtilService): class RadvdService(UtilService):
_name = "radvd" name = "radvd"
_configs = ("/etc/radvd/radvd.conf",) configs = ("/etc/radvd/radvd.conf",)
_dirs = ("/etc/radvd",) dirs = ("/etc/radvd",)
_startup = ("radvd -C /etc/radvd/radvd.conf -m logfile -l /var/log/radvd.log",) startup = ("radvd -C /etc/radvd/radvd.conf -m logfile -l /var/log/radvd.log",)
_shutdown = ("pkill radvd",) shutdown = ("pkill radvd",)
_validate = ("pidof radvd",) validate = ("pidof radvd",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -626,7 +626,7 @@ class RadvdService(UtilService):
""" """
cfg = "# auto-generated by RADVD service (utility.py)\n" cfg = "# auto-generated by RADVD service (utility.py)\n"
for ifc in node.netifs(): for ifc in node.netifs():
if hasattr(ifc, 'control') and ifc.control == True: if hasattr(ifc, 'control') and ifc.control is True:
continue continue
prefixes = map(cls.subnetentry, ifc.addrlist) prefixes = map(cls.subnetentry, ifc.addrlist)
if len(prefixes) < 1: if len(prefixes) < 1:
@ -671,11 +671,11 @@ class AtdService(UtilService):
""" """
Atd service for scheduling at jobs Atd service for scheduling at jobs
""" """
_name = "atd" name = "atd"
_configs = ("startatd.sh",) configs = ("startatd.sh",)
_dirs = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool") dirs = ("/var/spool/cron/atjobs", "/var/spool/cron/atspool")
_startup = ("sh startatd.sh",) startup = ("sh startatd.sh",)
_shutdown = ("pkill atd",) shutdown = ("pkill atd",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -692,6 +692,6 @@ class UserDefinedService(UtilService):
""" """
Dummy service allowing customization of anything. Dummy service allowing customization of anything.
""" """
_name = "UserDefined" name = "UserDefined"
_startindex = 50 startindex = 50
_meta = "Customize this service to do anything upon startup." meta = "Customize this service to do anything upon startup."

View file

@ -11,15 +11,15 @@ class XorpRtrmgr(CoreService):
XORP router manager service builds a config.boot file based on other XORP router manager service builds a config.boot file based on other
enabled XORP services, and launches necessary daemons upon startup. enabled XORP services, and launches necessary daemons upon startup.
""" """
_name = "xorp_rtrmgr" name = "xorp_rtrmgr"
_group = "XORP" group = "XORP"
_depends = () depends = ()
_dirs = ("/etc/xorp",) dirs = ("/etc/xorp",)
_configs = ("/etc/xorp/config.boot",) configs = ("/etc/xorp/config.boot",)
_startindex = 35 startindex = 35
_startup = ("xorp_rtrmgr -d -b %s -l /var/log/%s.log -P /var/run/%s.pid" % (_configs[0], _name, _name),) startup = ("xorp_rtrmgr -d -b %s -l /var/log/%s.log -P /var/run/%s.pid" % (configs[0], name, name),)
_shutdown = ("killall xorp_rtrmgr",) shutdown = ("killall xorp_rtrmgr",)
_validate = ("pidof xorp_rtrmgr",) validate = ("pidof xorp_rtrmgr",)
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -40,10 +40,10 @@ class XorpRtrmgr(CoreService):
for s in services: for s in services:
try: try:
s._depends.index(cls._name) s.depends.index(cls.name)
cfg += s.generatexorpconfig(node) cfg += s.generatexorpconfig(node)
except ValueError: except ValueError:
logger.exception("error getting value from service: %s", cls._name) logger.exception("error getting value from service: %s", cls.name)
return cfg return cfg
@ -74,15 +74,15 @@ class XorpService(CoreService):
Parent class for XORP services. Defines properties and methods Parent class for XORP services. Defines properties and methods
common to XORP's routing daemons. common to XORP's routing daemons.
""" """
_name = None name = None
_group = "XORP" group = "XORP"
_depends = ("xorp_rtrmgr",) depends = ("xorp_rtrmgr",)
_dirs = () dirs = ()
_configs = () configs = ()
_startindex = 40 startindex = 40
_startup = () startup = ()
_shutdown = () shutdown = ()
_meta = "The config file for this service can be found in the xorp_rtrmgr service." meta = "The config file for this service can be found in the xorp_rtrmgr service."
@staticmethod @staticmethod
def fea(forwarding): def fea(forwarding):
@ -165,7 +165,7 @@ class XorpOspfv2(XorpService):
not build its own configuration file but has hooks for adding to the not build its own configuration file but has hooks for adding to the
unified XORP configuration file. unified XORP configuration file.
""" """
_name = "XORP_OSPFv2" name = "XORP_OSPFv2"
@classmethod @classmethod
def generatexorpconfig(cls, node): def generatexorpconfig(cls, node):
@ -200,7 +200,7 @@ class XorpOspfv3(XorpService):
not build its own configuration file but has hooks for adding to the not build its own configuration file but has hooks for adding to the
unified XORP configuration file. unified XORP configuration file.
""" """
_name = "XORP_OSPFv3" name = "XORP_OSPFv3"
@classmethod @classmethod
def generatexorpconfig(cls, node): def generatexorpconfig(cls, node):
@ -227,8 +227,8 @@ class XorpBgp(XorpService):
""" """
IPv4 inter-domain routing. AS numbers and peers must be customized. IPv4 inter-domain routing. AS numbers and peers must be customized.
""" """
_name = "XORP_BGP" name = "XORP_BGP"
_custom_needed = True custom_needed = True
@classmethod @classmethod
def generatexorpconfig(cls, node): def generatexorpconfig(cls, node):
@ -257,7 +257,7 @@ class XorpRip(XorpService):
RIP IPv4 unicast routing. RIP IPv4 unicast routing.
""" """
_name = "XORP_RIP" name = "XORP_RIP"
@classmethod @classmethod
def generatexorpconfig(cls, node): def generatexorpconfig(cls, node):
@ -289,7 +289,7 @@ class XorpRipng(XorpService):
""" """
RIP NG IPv6 unicast routing. RIP NG IPv6 unicast routing.
""" """
_name = "XORP_RIPNG" name = "XORP_RIPNG"
@classmethod @classmethod
def generatexorpconfig(cls, node): def generatexorpconfig(cls, node):
@ -324,7 +324,7 @@ class XorpPimSm4(XorpService):
""" """
PIM Sparse Mode IPv4 multicast routing. PIM Sparse Mode IPv4 multicast routing.
""" """
_name = "XORP_PIMSM4" name = "XORP_PIMSM4"
@classmethod @classmethod
def generatexorpconfig(cls, node): def generatexorpconfig(cls, node):
@ -383,7 +383,7 @@ class XorpPimSm6(XorpService):
""" """
PIM Sparse Mode IPv6 multicast routing. PIM Sparse Mode IPv6 multicast routing.
""" """
_name = "XORP_PIMSM6" name = "XORP_PIMSM6"
@classmethod @classmethod
def generatexorpconfig(cls, node): def generatexorpconfig(cls, node):
@ -442,7 +442,7 @@ class XorpOlsr(XorpService):
""" """
OLSR IPv4 unicast MANET routing. OLSR IPv4 unicast MANET routing.
""" """
_name = "XORP_OLSR" name = "XORP_OLSR"
@classmethod @classmethod
def generatexorpconfig(cls, node): def generatexorpconfig(cls, node):

View file

@ -4,7 +4,7 @@ from core import logger
from core.conf import ConfigShim from core.conf import ConfigShim
from core.enumerations import NodeTypes from core.enumerations import NodeTypes
from core.misc import nodeutils from core.misc import nodeutils
from core.service import ServiceManager from core.service import ServiceManager, ServiceShim
from core.xml import xmlutils from core.xml import xmlutils
@ -316,7 +316,10 @@ class CoreDocumentParser0(object):
# associate nodes with services # associate nodes with services
for objid in sorted(svclists.keys()): for objid in sorted(svclists.keys()):
n = self.session.get_object(objid) n = self.session.get_object(objid)
self.session.services.addservicestonode(node=n, nodetype=n.type, services_str=svclists[objid]) services = svclists[objid]
if services:
services = services.split("|")
self.session.services.addservicestonode(node=n, node_type=n.type, services=services)
def parseservice(self, service, n): def parseservice(self, service, n):
""" """
@ -367,16 +370,20 @@ 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)
typestr = "service:%s:%s" % (name, filename) self.session.services.setservicefile(node_id=n.objid, service_name=name, filename=filename, data=data)
self.session.services.setservicefile(nodenum=n.objid, type=typestr,
filename=filename,
srcname=None, data=data)
if len(files): if len(files):
values.append("files=%s" % files) values.append("files=%s" % files)
if not bool(service.getAttribute("custom")): if not bool(service.getAttribute("custom")):
return True return True
values = ConfigShim.str_to_dict(values) self.session.services.setcustomservice(n.objid, svc)
self.session.services.setcustomservice(n.objid, svc, values) # set custom values for custom service
svc = self.session.services.getcustomservice(n.objid, None)
if not svc:
raise ValueError("custom service(%s) for node(%s) does not exist", svc.name, n.objid)
values = ConfigShim.str_to_dict("|".join(values))
for name, value in values.iteritems():
ServiceShim.setvalue(svc, name, value)
return True return True
def parsehooks(self, hooks): def parsehooks(self, hooks):

View file

@ -8,7 +8,7 @@ from core.conf import ConfigShim
from core.enumerations import NodeTypes from core.enumerations import NodeTypes
from core.misc import nodeutils from core.misc import nodeutils
from core.misc.ipaddress import MacAddress from core.misc.ipaddress import MacAddress
from core.service import ServiceManager from core.service import ServiceManager, ServiceShim
from core.xml import xmlutils from core.xml import xmlutils
@ -639,17 +639,19 @@ class CoreDocumentParser1(object):
custom = service.getAttribute('custom') custom = service.getAttribute('custom')
if custom and custom.lower() == 'true': if custom and custom.lower() == 'true':
values = ConfigShim.str_to_dict(values) self.session.services.setcustomservice(node.objid, session_service.name)
self.session.services.setcustomservice(node.objid, session_service, values) values = ConfigShim.str_to_dict("|".join(values))
for key, value in values.iteritems():
ServiceShim.setvalue(session_service, key, value)
# NOTE: if a custom service is used, setservicefile() must be # NOTE: if a custom service is used, setservicefile() must be
# called after the custom service exists # called after the custom service exists
for typestr, filename, data in files: for typestr, filename, data in files:
svcname = typestr.split(":")[1]
self.session.services.setservicefile( self.session.services.setservicefile(
nodenum=node.objid, node_id=node.objid,
type=typestr, service_name=svcname,
filename=filename, filename=filename,
srcname=None,
data=data data=data
) )
return str(name) return str(name)
@ -678,10 +680,13 @@ class CoreDocumentParser1(object):
services_str = None # default services will be added services_str = None # default services will be added
else: else:
return return
if services_str:
services_str = services_str.split("|")
self.session.services.addservicestonode( self.session.services.addservicestonode(
node=node, node=node,
nodetype=node_type, node_type=node_type,
services_str=services_str services=services_str
) )
def set_object_presentation(self, obj, element, node_type): def set_object_presentation(self, obj, element, node_type):

View file

@ -284,7 +284,7 @@ class CoreDocumentWriter0(Document):
for svc in defaults: for svc in defaults:
s = self.createElement("Service") s = self.createElement("Service")
spn.appendChild(s) spn.appendChild(s)
s.setAttribute("name", str(svc._name)) s.setAttribute("name", str(svc.name))
def addservices(self, node): def addservices(self, node):
""" """
@ -302,17 +302,17 @@ class CoreDocumentWriter0(Document):
for svc in node.services: for svc in node.services:
s = self.createElement("Service") s = self.createElement("Service")
spn.appendChild(s) spn.appendChild(s)
s.setAttribute("name", str(svc._name)) s.setAttribute("name", str(svc.name))
s.setAttribute("startup_idx", str(svc._startindex)) s.setAttribute("startup_idx", str(svc.startindex))
if svc._starttime != "": if svc.starttime != "":
s.setAttribute("start_time", str(svc._starttime)) s.setAttribute("start_time", str(svc.starttime))
# only record service names if not a customized service # only record service names if not a customized service
if not svc._custom: if not svc.custom:
continue continue
s.setAttribute("custom", str(svc._custom)) s.setAttribute("custom", str(svc.custom))
xmlutils.add_elements_from_list(self, s, svc._dirs, "Directory", "name") xmlutils.add_elements_from_list(self, s, svc.dirs, "Directory", "name")
for fn in svc._configs: for fn in svc.configs:
if len(fn) == 0: if len(fn) == 0:
continue continue
f = self.createElement("File") f = self.createElement("File")
@ -327,9 +327,9 @@ class CoreDocumentWriter0(Document):
txt = self.createTextNode(data) txt = self.createTextNode(data)
f.appendChild(txt) f.appendChild(txt)
xmlutils.add_text_elements_from_list(self, s, svc._startup, "Command", (("type", "start"),)) xmlutils.add_text_elements_from_list(self, s, svc.startup, "Command", (("type", "start"),))
xmlutils.add_text_elements_from_list(self, s, svc._shutdown, "Command", (("type", "stop"),)) xmlutils.add_text_elements_from_list(self, s, svc.shutdown, "Command", (("type", "stop"),))
xmlutils.add_text_elements_from_list(self, s, svc._validate, "Command", (("type", "validate"),)) xmlutils.add_text_elements_from_list(self, s, svc.validate, "Command", (("type", "validate"),))
def addaddresses(self, i, netif): def addaddresses(self, i, netif):
""" """

View file

@ -277,7 +277,7 @@ class ScenarioPlan(XmlElement):
for svc in defaults: for svc in defaults:
s = self.createElement("service") s = self.createElement("service")
spn.appendChild(s) spn.appendChild(s)
s.setAttribute("name", str(svc._name)) s.setAttribute("name", str(svc.name))
if defaultservices.hasChildNodes(): if defaultservices.hasChildNodes():
self.appendChild(defaultservices) self.appendChild(defaultservices)
@ -680,24 +680,24 @@ class DeviceElement(NamedXmlElement):
for svc in device_object.services: for svc in device_object.services:
s = self.createElement("service") s = self.createElement("service")
spn.appendChild(s) spn.appendChild(s)
s.setAttribute("name", str(svc._name)) s.setAttribute("name", str(svc.name))
s.setAttribute("startup_idx", str(svc._startindex)) s.setAttribute("startup_idx", str(svc.startindex))
if svc._starttime != "": if svc.starttime != "":
s.setAttribute("start_time", str(svc._starttime)) s.setAttribute("start_time", str(svc.starttime))
# only record service names if not a customized service # only record service names if not a customized service
if not svc._custom: if not svc.custom:
continue continue
s.setAttribute("custom", str(svc._custom)) s.setAttribute("custom", str(svc.custom))
xmlutils.add_elements_from_list(self, s, svc._dirs, "directory", "name") xmlutils.add_elements_from_list(self, s, svc.dirs, "directory", "name")
for fn in svc._configs: for fn in svc.configs:
if len(fn) == 0: if len(fn) == 0:
continue continue
f = self.createElement("file") f = self.createElement("file")
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 = self.coreSession.services.getservicefiledata(svc, fn) data = svc.configtxt.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
@ -705,12 +705,9 @@ class DeviceElement(NamedXmlElement):
txt = self.createTextNode("\n" + data) txt = self.createTextNode("\n" + data)
f.appendChild(txt) f.appendChild(txt)
xmlutils.add_text_elements_from_list(self, s, svc._startup, "command", xmlutils.add_text_elements_from_list(self, s, svc.startup, "command", (("type", "start"),))
(("type", "start"),)) xmlutils.add_text_elements_from_list(self, s, svc.shutdown, "command", (("type", "stop"),))
xmlutils.add_text_elements_from_list(self, s, svc._shutdown, "command", xmlutils.add_text_elements_from_list(self, s, svc.validate, "command", (("type", "validate"),))
(("type", "stop"),))
xmlutils.add_text_elements_from_list(self, s, svc._validate, "command",
(("type", "validate"),))
class ChannelElement(NamedXmlElement): class ChannelElement(NamedXmlElement):

View file

@ -159,7 +159,7 @@ def main():
n.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)]) n.newnetif(switch, ["%s/%s" % (prefix.addr(i), prefix.prefixlen)])
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.addservicestonode(n, "", options.services) session.services.addservicestonode(n, "", options.services.split("|"))
n.boot() n.boot()
nodelist.append(n) nodelist.append(n)
if i % 25 == 0: if i % 25 == 0:

View file

@ -429,8 +429,7 @@ class Experiment(object):
self.net.link(prev.netif(0), tmp.netif(0)) self.net.link(prev.netif(0), tmp.netif(0))
prev = tmp prev = tmp
def createemanesession(self, numnodes, verbose=False, cls=None, def createemanesession(self, numnodes, verbose=False, cls=None, values=None):
values=None):
""" Build a topology consisting of the given number of LxcNodes """ Build a topology consisting of the given number of LxcNodes
connected to an EMANE WLAN. connected to an EMANE WLAN.
""" """

View file

@ -7,7 +7,6 @@ import os
import pytest import pytest
from mock.mock import MagicMock from mock.mock import MagicMock
from core import services
from core.api.coreapi import CoreConfMessage from core.api.coreapi import CoreConfMessage
from core.api.coreapi import CoreEventMessage from core.api.coreapi import CoreEventMessage
from core.api.coreapi import CoreExecMessage from core.api.coreapi import CoreExecMessage
@ -29,6 +28,7 @@ from core.enumerations import NodeTlvs
from core.enumerations import NodeTypes from core.enumerations import NodeTypes
from core.misc import ipaddress from core.misc import ipaddress
from core.misc.ipaddress import MacAddress from core.misc.ipaddress import MacAddress
from core.service import ServiceManager
EMANE_SERVICES = "zebra|OSPFv3MDR|IPForward" EMANE_SERVICES = "zebra|OSPFv3MDR|IPForward"
@ -199,6 +199,7 @@ class CoreServerTest(object):
self.request_handler.handle_message(message) self.request_handler.handle_message(message)
def shutdown(self): def shutdown(self):
self.server.coreemu.shutdown()
self.server.shutdown() self.server.shutdown()
self.server.server_close() self.server.server_close()
@ -223,6 +224,9 @@ def session():
# shutdown coreemu # shutdown coreemu
coreemu.shutdown() coreemu.shutdown()
# clear services, since they will be reloaded
ServiceManager.services.clear()
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def ip_prefixes(): def ip_prefixes():
@ -231,9 +235,6 @@ def ip_prefixes():
@pytest.fixture() @pytest.fixture()
def cored(): def cored():
# load default services
services.load()
# create and return server # create and return server
server = CoreServerTest() server = CoreServerTest()
yield server yield server
@ -241,6 +242,11 @@ def cored():
# cleanup # cleanup
server.shutdown() server.shutdown()
#
# cleanup services
ServiceManager.services.clear()
def ping(from_node, to_node, ip_prefixes, count=3): def ping(from_node, to_node, ip_prefixes, count=3):
address = ip_prefixes.ip4_address(to_node) address = ip_prefixes.ip4_address(to_node)

View file

@ -6,22 +6,22 @@ from core.service import CoreService
class MyService(CoreService): class MyService(CoreService):
_name = "MyService" name = "MyService"
_group = "Utility" group = "Utility"
_depends = () depends = ()
_dirs = () dirs = ()
_configs = ('myservice.sh',) configs = ('myservice.sh',)
_startindex = 50 startindex = 50
_startup = ('sh myservice.sh',) startup = ('sh myservice.sh',)
_shutdown = () shutdown = ()
class MyService2(CoreService): class MyService2(CoreService):
_name = "MyService2" name = "MyService2"
_group = "Utility" group = "Utility"
_depends = () depends = ()
_dirs = () dirs = ()
_configs = ('myservice.sh',) configs = ('myservice.sh',)
_startindex = 50 startindex = 50
_startup = ('sh myservice.sh',) startup = ('sh myservice.sh',)
_shutdown = () shutdown = ()

View file

@ -15,10 +15,8 @@ from core.enumerations import NodeTypes
from core.mobility import BasicRangeModel from core.mobility import BasicRangeModel
from core.mobility import Ns2ScriptedMobility from core.mobility import Ns2ScriptedMobility
from core.netns.vnodeclient import VnodeClient from core.netns.vnodeclient import VnodeClient
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")
_MOBILITY_FILE = os.path.join(_PATH, "mobility.scen") _MOBILITY_FILE = os.path.join(_PATH, "mobility.scen")
_WIRED = [ _WIRED = [
NodeTypes.PEER_TO_PEER, NodeTypes.PEER_TO_PEER,
@ -51,16 +49,6 @@ def ping(from_node, to_node, ip_prefixes):
class TestCore: class TestCore:
def test_import_service(self):
"""
Test importing a custom service.
:param conftest.Core core: core fixture to test with
"""
ServiceManager.add_services(_SERVICES_PATH)
assert ServiceManager.get("MyService")
assert ServiceManager.get("MyService2")
@pytest.mark.parametrize("net_type", _WIRED) @pytest.mark.parametrize("net_type", _WIRED)
def test_wired_ping(self, session, net_type, ip_prefixes): def test_wired_ping(self, session, net_type, ip_prefixes):
""" """

View file

@ -101,7 +101,7 @@ def run_cmd(node, exec_cmd):
class TestGui: class TestGui:
def test_broker(self, session, cored): def test_broker(self, cored):
""" """
Test session broker creation. Test session broker creation.
@ -119,6 +119,7 @@ class TestGui:
daemon = "localhost" daemon = "localhost"
# add server # add server
session = cored.server.coreemu.create_session()
session.broker.addserver(daemon, "127.0.0.1", CORE_API_PORT) session.broker.addserver(daemon, "127.0.0.1", CORE_API_PORT)
# setup server # setup server

View file

@ -0,0 +1,18 @@
import os
from core.service import ServiceManager
_PATH = os.path.abspath(os.path.dirname(__file__))
_SERVICES_PATH = os.path.join(_PATH, "myservices")
class TestServices:
def test_import_service(self):
"""
Test importing a custom service.
:param conftest.Core core: core fixture to test with
"""
ServiceManager.add_services(_SERVICES_PATH)
assert ServiceManager.get("MyService")
assert ServiceManager.get("MyService2")

View file

@ -6,6 +6,7 @@ from core.emane.ieee80211abg import EmaneIeee80211abgModel
from core.emulator.emudata import NodeOptions from core.emulator.emudata import NodeOptions
from core.enumerations import NodeTypes from core.enumerations import NodeTypes
from core.mobility import BasicRangeModel from core.mobility import BasicRangeModel
from core.services.utility import SshService
_XML_VERSIONS = [ _XML_VERSIONS = [
"0.0", "0.0",
@ -68,6 +69,75 @@ class TestXml:
assert session.get_object(n1_id) assert session.get_object(n1_id)
assert session.get_object(n2_id) assert session.get_object(n2_id)
@pytest.mark.parametrize("version", _XML_VERSIONS)
def test_xml_ptp_services(self, session, tmpdir, version, ip_prefixes):
"""
Test xml client methods for a ptp neetwork.
:param session: session for test
:param tmpdir: tmpdir to create data in
:param str version: xml version to write and parse
:param ip_prefixes: generates ip addresses for nodes
"""
# create ptp
ptp_node = session.add_node(_type=NodeTypes.PEER_TO_PEER)
# create nodes
node_options = NodeOptions(model="host")
node_one = session.add_node(node_options=node_options)
node_two = session.add_node()
# link nodes to ptp net
for node in [node_one, node_two]:
interface = ip_prefixes.create_interface(node)
session.add_link(node.objid, ptp_node.objid, interface_one=interface)
# set custom values for node service\
custom_start = 50
session.services.setcustomservice(node_one.objid, SshService.name)
service = session.services.getcustomservice(node_one.objid, SshService.name)
service.startindex = custom_start
service_file = SshService.configs[0]
file_data = "# test"
session.services.setservicefile(node_one.objid, SshService.name, service_file, file_data)
# instantiate session
session.instantiate()
# get ids for nodes
n1_id = node_one.objid
n2_id = node_two.objid
# save xml
xml_file = tmpdir.join("session.xml")
file_path = xml_file.strpath
session.save_xml(file_path, version)
# verify xml file was created and can be parsed
assert xml_file.isfile()
assert ElementTree.parse(file_path)
# stop current session, clearing data
session.shutdown()
# verify nodes have been removed from session
with pytest.raises(KeyError):
assert not session.get_object(n1_id)
with pytest.raises(KeyError):
assert not session.get_object(n2_id)
# load saved xml
session.open_xml(file_path, start=True)
# retrieve custom service
service = session.services.getcustomservice(node_one.objid, SshService.name)
# verify nodes have been recreated
assert session.get_object(n1_id)
assert session.get_object(n2_id)
assert service.startindex == custom_start
assert service.configtxt.get(service_file) == file_data
@pytest.mark.parametrize("version", _XML_VERSIONS) @pytest.mark.parametrize("version", _XML_VERSIONS)
def test_xml_mobility(self, session, tmpdir, version, ip_prefixes): def test_xml_mobility(self, session, tmpdir, version, ip_prefixes):
""" """

View file

@ -16,13 +16,13 @@ import sys
import ns.core import ns.core
import ns.network import ns.network
from corens3.obj import Ns3Session
from corens3.obj import Ns3WifiNet
from core import logger from core import logger
from core.misc import ipaddress from core.misc import ipaddress
from core.misc import nodemaps from core.misc import nodemaps
from core.misc import nodeutils from core.misc import nodeutils
from corens3.obj import Ns3Session
from corens3.obj import Ns3WifiNet
def add_to_server(session): def add_to_server(session):
@ -60,7 +60,7 @@ def wifisession(opt):
node = session.addnode(name="n%d" % i) node = session.addnode(name="n%d" % i)
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.addservicestonode(node, "router", services_str) session.services.addservicestonode(node, "router", services_str.split("|"))
session.services.bootnodeservices(node) session.services.bootnodeservices(node)
session.setuprandomwalkmobility(bounds=(1000.0, 750.0, 0)) session.setuprandomwalkmobility(bounds=(1000.0, 750.0, 0))