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

@ -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