quick base to try and help flesh out documentation under core.*
This commit is contained in:
parent
8f45e5c4da
commit
4ae7958a63
15 changed files with 1956 additions and 292 deletions
|
@ -1,8 +1,8 @@
|
|||
"""
|
||||
service.py: definition of CoreService class that is subclassed to define
|
||||
Definition of CoreService class that is subclassed to define
|
||||
startup services and routing for nodes. A service is typically a daemon
|
||||
program launched when a node starts that provides some sort of
|
||||
service. The CoreServices class handles configuration messages for sending
|
||||
program launched when a node starts that provides some sort of service.
|
||||
The CoreServices class handles configuration messages for sending
|
||||
a list of available services to the GUI and for configuring individual
|
||||
services.
|
||||
"""
|
||||
|
@ -30,10 +30,19 @@ logger = log.get_logger(__name__)
|
|||
|
||||
|
||||
class ServiceManager(object):
|
||||
"""
|
||||
Manages services available for CORE nodes to use.
|
||||
"""
|
||||
services = []
|
||||
|
||||
@classmethod
|
||||
def add(cls, service):
|
||||
"""
|
||||
Add a service to manager.
|
||||
|
||||
:param CoreService service: service to add
|
||||
:return: nothing
|
||||
"""
|
||||
insert = 0
|
||||
for index, known_service in enumerate(cls.services):
|
||||
if known_service._group == service._group:
|
||||
|
@ -45,6 +54,13 @@ class ServiceManager(object):
|
|||
|
||||
@classmethod
|
||||
def get(cls, name):
|
||||
"""
|
||||
Retrieve a service from the manager.
|
||||
|
||||
:param str name: name of the service to retrieve
|
||||
:return: service if it exists, None otherwise
|
||||
:rtype: CoreService
|
||||
"""
|
||||
for service in cls.services:
|
||||
if service._name == name:
|
||||
return service
|
||||
|
@ -93,9 +109,13 @@ class CoreServices(ConfigurableManager):
|
|||
def importcustom(self, path):
|
||||
"""
|
||||
Import services from a myservices directory.
|
||||
|
||||
:param str path: path to import custom services from
|
||||
:return: nothing
|
||||
"""
|
||||
if not path or len(path) == 0:
|
||||
return
|
||||
|
||||
if not os.path.isdir(path):
|
||||
logger.warn("invalid custom service directory specified" ": %s" % path)
|
||||
return
|
||||
|
@ -124,6 +144,10 @@ class CoreServices(ConfigurableManager):
|
|||
"""
|
||||
Get the list of default services that should be enabled for a
|
||||
node for the given node type.
|
||||
|
||||
:param service_type: service type to get default services for
|
||||
:return: default services
|
||||
:rtype: list
|
||||
"""
|
||||
logger.debug("getting default services for type: %s", service_type)
|
||||
results = []
|
||||
|
@ -138,22 +162,31 @@ class CoreServices(ConfigurableManager):
|
|||
results.append(service)
|
||||
return results
|
||||
|
||||
def getcustomservice(self, objid, service):
|
||||
def getcustomservice(self, object_id, service):
|
||||
"""
|
||||
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.
|
||||
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
|
||||
:return: custom service from the node
|
||||
:rtype: CoreService
|
||||
"""
|
||||
if objid in self.customservices:
|
||||
for s in self.customservices[objid]:
|
||||
if object_id in self.customservices:
|
||||
for s in self.customservices[object_id]:
|
||||
if s._name == service._name:
|
||||
return s
|
||||
return service
|
||||
|
||||
def setcustomservice(self, objid, service, values):
|
||||
def setcustomservice(self, object_id, service, values):
|
||||
"""
|
||||
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 list values: values to
|
||||
:return:
|
||||
"""
|
||||
if service._custom:
|
||||
s = service
|
||||
|
@ -174,19 +207,24 @@ class CoreServices(ConfigurableManager):
|
|||
if service._custom:
|
||||
return
|
||||
# add the custom service to dict
|
||||
if objid in self.customservices:
|
||||
self.customservices[objid] += (s,)
|
||||
if object_id in self.customservices:
|
||||
self.customservices[object_id] += (s,)
|
||||
else:
|
||||
self.customservices[objid] = (s,)
|
||||
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
|
||||
"""
|
||||
if services_str is not None:
|
||||
services = services_str.split('|')
|
||||
services = services_str.split("|")
|
||||
for name in services:
|
||||
s = ServiceManager.get(name)
|
||||
if s is None:
|
||||
|
@ -202,35 +240,48 @@ class CoreServices(ConfigurableManager):
|
|||
s = self.getcustomservice(node.objid, s)
|
||||
node.addservice(s)
|
||||
|
||||
def getallconfigs(self):
|
||||
def getallconfigs(self, use_clsmap=True):
|
||||
"""
|
||||
Return (nodenum, service) tuples for all stored configs.
|
||||
Used when reconnecting to a session or opening XML.
|
||||
Return (nodenum, service) tuples for all stored configs. Used when reconnecting to a
|
||||
session or opening XML.
|
||||
|
||||
:param bool use_clsmap: should a class map be used, default to True
|
||||
:return: list of tuples of node ids and services
|
||||
:rtype: list
|
||||
"""
|
||||
r = []
|
||||
configs = []
|
||||
for nodenum in self.customservices:
|
||||
for s in self.customservices[nodenum]:
|
||||
r.append((nodenum, s))
|
||||
return r
|
||||
for service in self.customservices[nodenum]:
|
||||
configs.append((nodenum, service))
|
||||
return configs
|
||||
|
||||
def getallfiles(self, service):
|
||||
"""
|
||||
Return all customized files stored with a service.
|
||||
Used when reconnecting to a session or opening XML.
|
||||
|
||||
:param CoreService service: service to get files for
|
||||
:return:
|
||||
"""
|
||||
r = []
|
||||
files = []
|
||||
|
||||
if not service._custom:
|
||||
return r
|
||||
return files
|
||||
|
||||
for filename in service._configs:
|
||||
data = self.getservicefiledata(service, filename)
|
||||
if data is None:
|
||||
continue
|
||||
r.append((filename, data))
|
||||
return r
|
||||
files.append((filename, data))
|
||||
|
||||
return files
|
||||
|
||||
def bootnodeservices(self, node):
|
||||
"""
|
||||
Start all services on a node.
|
||||
|
||||
:param core.netns.nodes.CoreNode node: node to start services on
|
||||
:return:
|
||||
"""
|
||||
services = sorted(node.services, key=lambda service: service._startindex)
|
||||
use_startup_service = any(map(self.is_startup_service, services))
|
||||
|
@ -246,49 +297,66 @@ class CoreServices(ConfigurableManager):
|
|||
logger.exception("error converting start time to float")
|
||||
self.bootnodeservice(node, s, services, use_startup_service)
|
||||
|
||||
def bootnodeservice(self, node, s, services, use_startup_service):
|
||||
def bootnodeservice(self, node, service, services, use_startup_service):
|
||||
"""
|
||||
Start a service on a node. Create private dirs, generate config
|
||||
files, and execute startup commands.
|
||||
|
||||
:param core.netns.nodes.CoreNode node: node to boot services on
|
||||
:param CoreService service: service to start
|
||||
:param list services: service list
|
||||
:param bool use_startup_service: flag to use startup services or not
|
||||
:return: nothing
|
||||
"""
|
||||
if s._custom:
|
||||
self.bootnodecustomservice(node, s, services, use_startup_service)
|
||||
if service._custom:
|
||||
self.bootnodecustomservice(node, service, services, use_startup_service)
|
||||
return
|
||||
logger.info("starting service %s (%s)" % (s._name, s._startindex))
|
||||
for d in s._dirs:
|
||||
|
||||
logger.info("starting service %s (%s)" % (service._name, service._startindex))
|
||||
for directory in service._dirs:
|
||||
try:
|
||||
node.privatedir(d)
|
||||
node.privatedir(directory)
|
||||
except:
|
||||
logger.exception("Error making node %s dir %s", node.name, d)
|
||||
for filename in s.getconfigfilenames(node.objid, services):
|
||||
cfg = s.generateconfig(node, filename, services)
|
||||
logger.exception("Error making node %s dir %s", node.name, directory)
|
||||
|
||||
for filename in service.getconfigfilenames(node.objid, services):
|
||||
cfg = service.generateconfig(node, filename, services)
|
||||
node.nodefile(filename, cfg)
|
||||
if use_startup_service and not self.is_startup_service(s):
|
||||
|
||||
if use_startup_service and not self.is_startup_service(service):
|
||||
return
|
||||
for cmd in s.getstartup(node, services):
|
||||
|
||||
for cmd in service.getstartup(node, services):
|
||||
try:
|
||||
# NOTE: this wait=False can be problematic!
|
||||
node.cmd(shlex.split(cmd), wait=False)
|
||||
except:
|
||||
logger.exception("error starting command %s", cmd)
|
||||
|
||||
def bootnodecustomservice(self, node, s, services, use_startup_service):
|
||||
def bootnodecustomservice(self, node, service, services, use_startup_service):
|
||||
"""
|
||||
Start a custom service on a node. Create private dirs, use supplied
|
||||
config files, and execute supplied startup commands.
|
||||
|
||||
:param core.netns.nodes.CoreNode node: node to boot services on
|
||||
:param CoreService service: service to start
|
||||
:param list services: service list
|
||||
:param bool use_startup_service: flag to use startup services or not
|
||||
:return: nothing
|
||||
"""
|
||||
logger.info("starting service %s (%s)(custom)" % (s._name, s._startindex))
|
||||
for d in s._dirs:
|
||||
logger.info("starting service %s (%s)(custom)" % (service._name, service._startindex))
|
||||
for directory in service._dirs:
|
||||
try:
|
||||
node.privatedir(d)
|
||||
node.privatedir(directory)
|
||||
except:
|
||||
logger.exception("Error making node %s dir %s", node.name, d)
|
||||
for i, filename in enumerate(s._configs):
|
||||
logger.exception("Error making node %s dir %s", node.name, directory)
|
||||
|
||||
for i, filename in enumerate(service._configs):
|
||||
if len(filename) == 0:
|
||||
continue
|
||||
cfg = self.getservicefiledata(s, filename)
|
||||
cfg = self.getservicefiledata(service, filename)
|
||||
if cfg is None:
|
||||
cfg = s.generateconfig(node, filename, services)
|
||||
cfg = service.generateconfig(node, filename, services)
|
||||
# cfg may have a file:/// url for copying from a file
|
||||
try:
|
||||
if self.copyservicefile(node, filename, cfg):
|
||||
|
@ -298,10 +366,10 @@ class CoreServices(ConfigurableManager):
|
|||
continue
|
||||
node.nodefile(filename, cfg)
|
||||
|
||||
if use_startup_service and not self.is_startup_service(s):
|
||||
if use_startup_service and not self.is_startup_service(service):
|
||||
return
|
||||
|
||||
for cmd in s._startup:
|
||||
for cmd in service._startup:
|
||||
try:
|
||||
# NOTE: this wait=False can be problematic!
|
||||
node.cmd(shlex.split(cmd), wait=False)
|
||||
|
@ -313,6 +381,12 @@ class CoreServices(ConfigurableManager):
|
|||
Given a configured service filename and config, determine if the
|
||||
config references an existing file that should be copied.
|
||||
Returns True for local files, False for generated.
|
||||
|
||||
:param core.netns.nodes.CoreNode node: node to copy service for
|
||||
:param str filename: file name for a configured service
|
||||
:param str cfg: configuration string
|
||||
:return: True if successful, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
if cfg[:7] == 'file://':
|
||||
src = cfg[7:]
|
||||
|
@ -326,6 +400,9 @@ class CoreServices(ConfigurableManager):
|
|||
def validatenodeservices(self, node):
|
||||
"""
|
||||
Run validation commands for all services on a node.
|
||||
|
||||
:param core.netns.nodes.CoreNode node: node to validate services for
|
||||
:return: nothing
|
||||
"""
|
||||
services = sorted(node.services, key=lambda service: service._startindex)
|
||||
for s in services:
|
||||
|
@ -334,6 +411,12 @@ class CoreServices(ConfigurableManager):
|
|||
def validatenodeservice(self, node, service, services):
|
||||
"""
|
||||
Run the validation command(s) for a service.
|
||||
|
||||
:param core.netns.nodes.CoreNode node: node to validate service for
|
||||
:param CoreService service: service to validate
|
||||
:param list services: services for node
|
||||
:return: service validation status
|
||||
:rtype: int
|
||||
"""
|
||||
logger.info("validating service for node (%s - %s): %s (%s)",
|
||||
node.objid, node.name, service._name, service._startindex)
|
||||
|
@ -360,21 +443,29 @@ class CoreServices(ConfigurableManager):
|
|||
def stopnodeservices(self, node):
|
||||
"""
|
||||
Stop all services on a node.
|
||||
|
||||
: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)
|
||||
|
||||
def stopnodeservice(self, node, s):
|
||||
def stopnodeservice(self, node, service):
|
||||
"""
|
||||
Stop a service on a node.
|
||||
|
||||
:param core.netns.nodes.CoreNode node: node to stop a service on
|
||||
:param CoreService service: service to stop
|
||||
:return: status for stopping the services
|
||||
:rtype: str
|
||||
"""
|
||||
status = ""
|
||||
if len(s._shutdown) == 0:
|
||||
if len(service._shutdown) == 0:
|
||||
# doesn't have a shutdown command
|
||||
status += "0"
|
||||
else:
|
||||
for cmd in s._shutdown:
|
||||
for cmd in service._shutdown:
|
||||
try:
|
||||
tmp = node.cmd(shlex.split(cmd), wait=True)
|
||||
status += "%s" % tmp
|
||||
|
@ -392,6 +483,7 @@ class CoreServices(ConfigurableManager):
|
|||
|
||||
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
||||
:return: response messages
|
||||
:rtype: ConfigData
|
||||
"""
|
||||
node_id = config_data.node
|
||||
session_id = config_data.session
|
||||
|
@ -525,9 +617,14 @@ class CoreServices(ConfigurableManager):
|
|||
|
||||
return None
|
||||
|
||||
def servicesfromopaque(self, opaque, objid):
|
||||
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 = []
|
||||
|
@ -537,7 +634,7 @@ class CoreServices(ConfigurableManager):
|
|||
servicenames = servicesstring[1].split(',')
|
||||
for name in servicenames:
|
||||
s = ServiceManager.get(name)
|
||||
s = self.getcustomservice(objid, s)
|
||||
s = self.getcustomservice(object_id, s)
|
||||
if s is None:
|
||||
unknown.append(name)
|
||||
else:
|
||||
|
@ -551,6 +648,10 @@ class CoreServices(ConfigurableManager):
|
|||
"title1:1-5|title2:6-9|10-12", where title is an optional group title
|
||||
and i-j is a numeric range of value indices; groups are
|
||||
separated by commas.
|
||||
|
||||
:param list servicelist: service list to build group string from
|
||||
:return: groups string
|
||||
:rtype: str
|
||||
"""
|
||||
i = 0
|
||||
r = ""
|
||||
|
@ -573,10 +674,16 @@ class CoreServices(ConfigurableManager):
|
|||
r += "-%d" % i
|
||||
return r
|
||||
|
||||
# TODO: need to remove depenency on old message structure below
|
||||
def getservicefile(self, services, node, filename):
|
||||
"""
|
||||
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 core.netns.nodes.CoreNode node: node to get service file from
|
||||
:param str filename: file name to retrieve
|
||||
:return: file message for node
|
||||
"""
|
||||
svc = services[0]
|
||||
# get the filename and determine the config file index
|
||||
|
@ -609,6 +716,10 @@ class CoreServices(ConfigurableManager):
|
|||
"""
|
||||
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)
|
||||
|
@ -623,6 +734,13 @@ class CoreServices(ConfigurableManager):
|
|||
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 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.")
|
||||
|
@ -653,6 +771,9 @@ class CoreServices(ConfigurableManager):
|
|||
"""
|
||||
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
|
||||
|
@ -670,7 +791,7 @@ class CoreServices(ConfigurableManager):
|
|||
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)
|
||||
fail += "Stop %s," % s._name
|
||||
if event_type == EventTypes.START.value or event_type == EventTypes.RESTART.value:
|
||||
if s._custom:
|
||||
cmds = s._startup
|
||||
|
@ -779,6 +900,11 @@ class CoreService(object):
|
|||
Return the tuple of configuration file filenames. This default method
|
||||
returns the cls._configs tuple, but this method may be overriden to
|
||||
provide node-specific filenames that may be based on other services.
|
||||
|
||||
:param int nodenum: node id to get config file names for
|
||||
:param list services: node services
|
||||
:return: class configuration files
|
||||
:rtype: tuple
|
||||
"""
|
||||
return cls._configs
|
||||
|
||||
|
@ -790,6 +916,11 @@ class CoreService(object):
|
|||
provided to allow interdependencies (e.g. zebra and OSPF).
|
||||
Return the configuration string to be written to a file or sent
|
||||
to the GUI for customization.
|
||||
|
||||
:param core.netns.nodes.CoreNode node: node to generate config for
|
||||
:param str filename: file name to generate config for
|
||||
:param list services: services for node
|
||||
:return: nothing
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -798,8 +929,13 @@ class CoreService(object):
|
|||
"""
|
||||
Return the tuple of startup commands. This default method
|
||||
returns the cls._startup tuple, but this method may be
|
||||
overriden to provide node-specific commands that may be
|
||||
overridden to provide node-specific commands that may be
|
||||
based on other services.
|
||||
|
||||
:param core.netns.nodes.CoreNode node: node to get startup for
|
||||
:param list services: services for node
|
||||
:return: startup commands
|
||||
:rtype: tuple
|
||||
"""
|
||||
return cls._startup
|
||||
|
||||
|
@ -810,6 +946,11 @@ class CoreService(object):
|
|||
returns the cls._validate tuple, but this method may be
|
||||
overriden to provide node-specific commands that may be
|
||||
based on other services.
|
||||
|
||||
:param core.netns.nodes.CoreNode node: node to validate
|
||||
:param list services: services for node
|
||||
:return: validation commands
|
||||
:rtype: tuple
|
||||
"""
|
||||
return cls._validate
|
||||
|
||||
|
@ -818,6 +959,11 @@ class CoreService(object):
|
|||
"""
|
||||
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]
|
||||
|
@ -834,6 +980,9 @@ class CoreService(object):
|
|||
"""
|
||||
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:
|
||||
|
@ -844,6 +993,13 @@ class CoreService(object):
|
|||
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
|
||||
# this handles data conversion to int, string, and tuples
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue