refactored service interaction use names with a _, and cleanup up some of the CoreServices methods
This commit is contained in:
parent
0bf9c99910
commit
e80736061f
27 changed files with 958 additions and 885 deletions
|
@ -6,18 +6,110 @@ The CoreServices class handles configuration messages for sending
|
|||
a list of available services to the GUI and for configuring individual
|
||||
services.
|
||||
"""
|
||||
import time
|
||||
|
||||
from core import CoreCommandError
|
||||
from core import logger
|
||||
from core.data import EventData
|
||||
from core.data import FileData
|
||||
from core.enumerations import EventTypes
|
||||
from core.enumerations import MessageFlags
|
||||
from core.enumerations import RegisterTlvs
|
||||
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):
|
||||
"""
|
||||
Manages services available for CORE nodes to use.
|
||||
|
@ -33,7 +125,7 @@ class ServiceManager(object):
|
|||
:return: nothing
|
||||
"""
|
||||
logger.info("loading service: %s", service.__name__)
|
||||
name = service._name
|
||||
name = service.name
|
||||
if name in cls.services:
|
||||
raise ValueError("duplicate service being added: %s" % name)
|
||||
cls.services[name] = service
|
||||
|
@ -45,7 +137,7 @@ class ServiceManager(object):
|
|||
|
||||
:param str name: name of the service to retrieve
|
||||
:return: service if it exists, None otherwise
|
||||
:rtype: CoreService
|
||||
:rtype: CoreService.class
|
||||
"""
|
||||
return cls.services.get(name)
|
||||
|
||||
|
@ -60,7 +152,7 @@ class ServiceManager(object):
|
|||
"""
|
||||
services = utils.load_classes(path, CoreService)
|
||||
for service in services:
|
||||
if not service._name:
|
||||
if not service.name:
|
||||
continue
|
||||
service.on_load()
|
||||
cls.add(service)
|
||||
|
@ -76,7 +168,6 @@ class CoreServices(object):
|
|||
"""
|
||||
name = "services"
|
||||
config_type = RegisterTlvs.UTILITY.value
|
||||
_invalid_custom_names = ("core", "api", "emane", "misc", "netns", "phys", "services")
|
||||
|
||||
def __init__(self, session):
|
||||
"""
|
||||
|
@ -84,7 +175,6 @@ class CoreServices(object):
|
|||
|
||||
:param core.session.Session session: session this manager is tied to
|
||||
"""
|
||||
# ConfigurableManager.__init__(self)
|
||||
self.session = session
|
||||
# dict of default services tuples, key is node type
|
||||
self.defaultservices = {}
|
||||
|
@ -109,99 +199,79 @@ class CoreServices(object):
|
|||
|
||||
:param service_type: service type to get default services for
|
||||
:return: default services
|
||||
:rtype: list
|
||||
:rtype: list[CoreService]
|
||||
"""
|
||||
logger.debug("getting default services for type: %s", service_type)
|
||||
results = []
|
||||
if service_type in self.defaultservices:
|
||||
defaults = self.defaultservices[service_type]
|
||||
for name in defaults:
|
||||
logger.debug("checking for service with service manager: %s", name)
|
||||
service = ServiceManager.get(name)
|
||||
if not service:
|
||||
logger.warn("default service %s is unknown", name)
|
||||
else:
|
||||
results.append(service)
|
||||
defaults = self.defaultservices.get(service_type, [])
|
||||
for name in defaults:
|
||||
logger.debug("checking for service with service manager: %s", name)
|
||||
service = ServiceManager.get(name)
|
||||
if not service:
|
||||
logger.warn("default service %s is unknown", name)
|
||||
else:
|
||||
results.append(service)
|
||||
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.
|
||||
If no custom service is found, return the specified service.
|
||||
|
||||
:param int object_id: object id to get service from
|
||||
:param CoreService service: custom service to retrieve
|
||||
:param int node_id: object id to get service from
|
||||
: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
|
||||
:rtype: CoreService
|
||||
"""
|
||||
if object_id in self.customservices:
|
||||
for s in self.customservices[object_id]:
|
||||
if s._name == service._name:
|
||||
return s
|
||||
return service
|
||||
node_services = self.customservices.setdefault(node_id, {})
|
||||
default = None
|
||||
if default_service:
|
||||
default = ServiceManager.get(service_name)
|
||||
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
|
||||
using a list of values that came from a config message.
|
||||
|
||||
:param int object_id: object id to set custom service for
|
||||
:param class service: 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
|
||||
:param int node_id: object id to set custom service for
|
||||
:param str service_name: name of service to set
|
||||
:return: nothing
|
||||
"""
|
||||
if services_str is not None:
|
||||
logger.info("setting custom services for node(%s)", node.name)
|
||||
services = services_str.split("|")
|
||||
for name in services:
|
||||
s = ServiceManager.get(name)
|
||||
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)
|
||||
logger.debug("setting custom service(%s) for node: %s", node_id, service_name)
|
||||
service = self.getcustomservice(node_id, service_name)
|
||||
if not service:
|
||||
service_class = ServiceManager.get(service_name)
|
||||
service = service_class()
|
||||
|
||||
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
|
||||
session or opening XML.
|
||||
|
@ -211,9 +281,9 @@ class CoreServices(object):
|
|||
:rtype: list
|
||||
"""
|
||||
configs = []
|
||||
for nodenum in self.customservices:
|
||||
for service in self.customservices[nodenum]:
|
||||
configs.append((nodenum, service))
|
||||
for node_id in self.customservices.iterkeys():
|
||||
for service in self.customservices[node_id].itervalues():
|
||||
configs.append((node_id, service))
|
||||
return configs
|
||||
|
||||
def getallfiles(self, service):
|
||||
|
@ -226,11 +296,11 @@ class CoreServices(object):
|
|||
"""
|
||||
files = []
|
||||
|
||||
if not service._custom:
|
||||
if not service.custom:
|
||||
return files
|
||||
|
||||
for filename in service._configs:
|
||||
data = self.getservicefiledata(service, filename)
|
||||
for filename in service.configs:
|
||||
data = service.configtxt.get(filename)
|
||||
if data is None:
|
||||
continue
|
||||
files.append((filename, data))
|
||||
|
@ -244,19 +314,19 @@ class CoreServices(object):
|
|||
:param core.netns.vnode.LxcNode node: node to start services on
|
||||
: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))
|
||||
for s in services:
|
||||
if len(str(s._starttime)) > 0:
|
||||
for service in services:
|
||||
if len(str(service.starttime)) > 0:
|
||||
try:
|
||||
t = float(s._starttime)
|
||||
if t > 0.0:
|
||||
starttime = float(service.starttime)
|
||||
if starttime > 0.0:
|
||||
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
|
||||
except ValueError:
|
||||
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):
|
||||
"""
|
||||
|
@ -269,12 +339,12 @@ class CoreServices(object):
|
|||
:param bool use_startup_service: flag to use startup services or not
|
||||
:return: nothing
|
||||
"""
|
||||
if service._custom:
|
||||
if service.custom:
|
||||
self.bootnodecustomservice(node, service, services, use_startup_service)
|
||||
return
|
||||
|
||||
logger.info("starting node(%s) service: %s (%s)", node.name, service._name, service._startindex)
|
||||
for directory in service._dirs:
|
||||
logger.info("starting node(%s) service: %s (%s)", node.name, service.name, service.startindex)
|
||||
for directory in service.dirs:
|
||||
node.privatedir(directory)
|
||||
|
||||
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
|
||||
:return: nothing
|
||||
"""
|
||||
logger.info("starting service(%s) %s (%s)(custom)", service, service._name, service._startindex)
|
||||
for directory in service._dirs:
|
||||
logger.info("starting node(%s) service(custom): %s (%s)", node.name, service.name, service.startindex)
|
||||
for directory in service.dirs:
|
||||
node.privatedir(directory)
|
||||
|
||||
logger.info("service configurations: %s", service._configs)
|
||||
for i, filename in enumerate(service._configs):
|
||||
logger.info("service configurations: %s", service.configs)
|
||||
for filename in service.configs:
|
||||
logger.info("generating service config: %s", filename)
|
||||
if len(filename) == 0:
|
||||
continue
|
||||
cfg = self.getservicefiledata(service, filename)
|
||||
cfg = service.configtxt.get(filename)
|
||||
if cfg is None:
|
||||
cfg = service.generateconfig(node, filename, services)
|
||||
|
||||
# cfg may have a file:/// url for copying from a file
|
||||
try:
|
||||
if self.copyservicefile(node, filename, cfg):
|
||||
|
@ -323,7 +392,7 @@ class CoreServices(object):
|
|||
if use_startup_service and not self.is_startup_service(service):
|
||||
return
|
||||
|
||||
for args in service._startup:
|
||||
for args in service.startup:
|
||||
# TODO: this wait=False can be problematic!
|
||||
node.cmd(args, wait=False)
|
||||
|
||||
|
@ -355,9 +424,9 @@ class CoreServices(object):
|
|||
:param core.netns.vnode.LxcNode node: node to validate services for
|
||||
:return: nothing
|
||||
"""
|
||||
services = sorted(node.services, key=lambda service: service._startindex)
|
||||
for s in services:
|
||||
self.validatenodeservice(node, s, services)
|
||||
services = sorted(node.services, key=lambda service: service.startindex)
|
||||
for service in services:
|
||||
self.validatenodeservice(node, service, services)
|
||||
|
||||
def validatenodeservice(self, node, service, services):
|
||||
"""
|
||||
|
@ -369,22 +438,20 @@ class CoreServices(object):
|
|||
:return: service validation status
|
||||
:rtype: int
|
||||
"""
|
||||
logger.info("validating service for node (%s): %s (%s)", node.name, service._name, service._startindex)
|
||||
if service._custom:
|
||||
validate_cmds = service._validate
|
||||
logger.info("validating service for node (%s): %s (%s)", node.name, service.name, service.startindex)
|
||||
if service.custom:
|
||||
validate_cmds = service.validate
|
||||
else:
|
||||
validate_cmds = service.getvalidate(node, services)
|
||||
|
||||
status = 0
|
||||
# has validate commands
|
||||
if validate_cmds:
|
||||
for args in validate_cmds:
|
||||
logger.info("validating service %s using: %s", service._name, args)
|
||||
try:
|
||||
node.check_cmd(args)
|
||||
except CoreCommandError:
|
||||
logger.exception("validate command failed")
|
||||
status = -1
|
||||
for args in validate_cmds:
|
||||
logger.info("validating service %s using: %s", service.name, args)
|
||||
try:
|
||||
node.check_cmd(args)
|
||||
except CoreCommandError:
|
||||
logger.exception("validate command failed")
|
||||
status = -1
|
||||
|
||||
return status
|
||||
|
||||
|
@ -395,9 +462,9 @@ class CoreServices(object):
|
|||
:param core.netns.nodes.CoreNode node: node to stop services on
|
||||
:return: nothing
|
||||
"""
|
||||
services = sorted(node.services, key=lambda service: service._startindex)
|
||||
for s in services:
|
||||
self.stopnodeservice(node, s)
|
||||
services = sorted(node.services, key=lambda x: x.startindex)
|
||||
for service in services:
|
||||
self.stopnodeservice(node, service)
|
||||
|
||||
def stopnodeservice(self, node, service):
|
||||
"""
|
||||
|
@ -409,68 +476,57 @@ class CoreServices(object):
|
|||
:rtype: str
|
||||
"""
|
||||
status = "0"
|
||||
if service._shutdown:
|
||||
for args in service._shutdown:
|
||||
try:
|
||||
node.check_cmd(args)
|
||||
except CoreCommandError:
|
||||
logger.exception("error running stop command %s", args)
|
||||
# TODO: determine if its ok to just return the bad exit status
|
||||
status = "-1"
|
||||
for args in service.shutdown:
|
||||
try:
|
||||
node.check_cmd(args)
|
||||
except CoreCommandError:
|
||||
logger.exception("error running stop command %s", args)
|
||||
# TODO: determine if its ok to just return the bad exit status
|
||||
status = "-1"
|
||||
return status
|
||||
|
||||
def servicesfromopaque(self, opaque, object_id):
|
||||
"""
|
||||
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):
|
||||
def getservicefile(self, service_name, node, filename, services):
|
||||
"""
|
||||
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.
|
||||
|
||||
: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 str filename: file name to retrieve
|
||||
:param list[str] services: list of services associated with node
|
||||
:return: file message for node
|
||||
"""
|
||||
svc = services[0]
|
||||
# get the filename and determine the config file index
|
||||
if svc._custom:
|
||||
cfgfiles = svc._configs
|
||||
# get service to get file from
|
||||
service = self.getcustomservice(node.objid, service_name, default_service=True)
|
||||
if not service:
|
||||
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:
|
||||
cfgfiles = svc.getconfigfilenames(node.objid, services)
|
||||
if filename not in cfgfiles:
|
||||
logger.warn("Request for unknown file '%s' for service '%s'" % (filename, services[0]))
|
||||
return None
|
||||
config_files = service.getconfigfilenames(node.objid, node_services)
|
||||
|
||||
if filename not in config_files:
|
||||
raise ValueError("unknown service(%s) config file: %s", service_name, filename)
|
||||
|
||||
# get the file data
|
||||
data = self.getservicefiledata(svc, filename)
|
||||
data = service.configtxt.get(filename)
|
||||
if data is None:
|
||||
data = "%s" % svc.generateconfig(node, filename, services)
|
||||
data = "%s" % service.generateconfig(node, filename, services)
|
||||
else:
|
||||
data = "%s" % data
|
||||
filetypestr = "service:%s" % svc._name
|
||||
|
||||
filetypestr = "service:%s" % service.name
|
||||
return FileData(
|
||||
message_type=MessageFlags.ADD.value,
|
||||
node=node.objid,
|
||||
|
@ -479,141 +535,85 @@ class CoreServices(object):
|
|||
data=data
|
||||
)
|
||||
|
||||
def getservicefiledata(self, service, filename):
|
||||
"""
|
||||
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):
|
||||
def setservicefile(self, node_id, service_name, filename, data):
|
||||
"""
|
||||
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
|
||||
config files in the service.
|
||||
|
||||
:param int nodenum: node id to set service file
|
||||
:param str type: file type to set
|
||||
:param int node_id: node id to set service file
|
||||
:param str service_name: service name to set file for
|
||||
:param str filename: file name to set
|
||||
:param str srcname: source name of file to set
|
||||
:param data: data for file to set
|
||||
:return: nothing
|
||||
"""
|
||||
if len(type.split(':')) < 2:
|
||||
logger.warn("Received file type did not contain service info.")
|
||||
return
|
||||
if srcname is not None:
|
||||
raise NotImplementedError
|
||||
svcid, svcname = type.split(':')[:2]
|
||||
svc = ServiceManager.get(svcname)
|
||||
svc = self.getcustomservice(nodenum, svc)
|
||||
# attempt to set custom service, if needed
|
||||
self.setcustomservice(node_id, service_name)
|
||||
|
||||
# retrieve custom service
|
||||
svc = self.getcustomservice(node_id, service_name)
|
||||
if svc is None:
|
||||
logger.warn("Received filename for unknown service '%s'" % svcname)
|
||||
logger.warn("received filename for unknown service: %s", service_name)
|
||||
return
|
||||
cfgfiles = svc._configs
|
||||
|
||||
# validate file being set is valid
|
||||
cfgfiles = svc.configs
|
||||
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
|
||||
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
|
||||
a service on a given node.
|
||||
Startup a node service.
|
||||
|
||||
: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
|
||||
"""
|
||||
event_type = event_data.event_type
|
||||
node_id = event_data.node
|
||||
name = event_data.name
|
||||
if service.custom:
|
||||
cfgfiles = service.configs
|
||||
else:
|
||||
cfgfiles = service.getconfigfilenames(node.objid, services)
|
||||
|
||||
try:
|
||||
node = self.session.get_object(node_id)
|
||||
except KeyError:
|
||||
logger.warn("Ignoring event for service '%s', unknown node '%s'", name, node_id)
|
||||
return
|
||||
for filename in cfgfiles:
|
||||
if filename[:7] == "file:///":
|
||||
# TODO: implement this
|
||||
raise NotImplementedError
|
||||
|
||||
fail = ""
|
||||
services, unknown = self.servicesfromopaque(name, node_id)
|
||||
for s in 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)
|
||||
cfg = service.configtxt.get(filename)
|
||||
if cfg is None:
|
||||
cfg = service.generateconfig(node, filename, services)
|
||||
|
||||
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)
|
||||
node.nodefile(filename, cfg)
|
||||
|
||||
|
||||
class CoreService(object):
|
||||
|
@ -621,45 +621,42 @@ class CoreService(object):
|
|||
Parent class used for defining services.
|
||||
"""
|
||||
# service name should not include spaces
|
||||
_name = ""
|
||||
name = None
|
||||
|
||||
# group string allows grouping services together
|
||||
_group = ""
|
||||
group = None
|
||||
|
||||
# list name(s) of services that this service depends upon
|
||||
_depends = ()
|
||||
keys = ["dirs", "files", "startidx", "cmdup", "cmddown", "cmdval", "meta", "starttime"]
|
||||
depends = ()
|
||||
|
||||
# private, per-node directories required by this service
|
||||
_dirs = ()
|
||||
dirs = ()
|
||||
|
||||
# config files written by this service
|
||||
_configs = ()
|
||||
# configs = []
|
||||
configs = ()
|
||||
|
||||
# index used to determine start order with other services
|
||||
_startindex = 0
|
||||
startindex = 0
|
||||
|
||||
# time in seconds after runtime to run startup commands
|
||||
_starttime = ""
|
||||
starttime = 0
|
||||
|
||||
# list of startup commands
|
||||
_startup = ()
|
||||
# startup = []
|
||||
startup = ()
|
||||
|
||||
# list of shutdown commands
|
||||
_shutdown = ()
|
||||
shutdown = ()
|
||||
|
||||
# list of validate commands
|
||||
_validate = ()
|
||||
validate = ()
|
||||
|
||||
# metadata associated with this service
|
||||
_meta = ""
|
||||
meta = None
|
||||
|
||||
# custom configuration text
|
||||
_configtxt = ()
|
||||
_custom = False
|
||||
_custom_needed = False
|
||||
configtxt = {}
|
||||
custom = False
|
||||
custom_needed = False
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
|
@ -667,7 +664,16 @@ class CoreService(object):
|
|||
against their config. Services are instantiated when a custom
|
||||
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
|
||||
def on_load(cls):
|
||||
|
@ -685,7 +691,7 @@ class CoreService(object):
|
|||
:return: class configuration files
|
||||
:rtype: tuple
|
||||
"""
|
||||
return cls._configs
|
||||
return cls.configs
|
||||
|
||||
@classmethod
|
||||
def generateconfig(cls, node, filename, services):
|
||||
|
@ -716,7 +722,7 @@ class CoreService(object):
|
|||
:return: startup commands
|
||||
:rtype: tuple
|
||||
"""
|
||||
return cls._startup
|
||||
return cls.startup
|
||||
|
||||
@classmethod
|
||||
def getvalidate(cls, node, services):
|
||||
|
@ -731,76 +737,4 @@ class CoreService(object):
|
|||
:return: validation commands
|
||||
:rtype: tuple
|
||||
"""
|
||||
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
|
||||
return cls.validate
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue