added service executable check, added error message to gui for service load errors

This commit is contained in:
Blake J. Harnden 2018-06-19 09:19:49 -07:00
parent e80736061f
commit bf47e5fc0d
10 changed files with 90 additions and 39 deletions

View file

@ -17,13 +17,13 @@ from core import logger
from core.api import coreapi
from core.api import dataconversion
from core.conf import ConfigShim
from core.data import ConfigData
from core.data import ConfigData, ExceptionData
from core.data import EventData
from core.data import FileData
from core.emulator.emudata import InterfaceData
from core.emulator.emudata import LinkOptions
from core.emulator.emudata import NodeOptions
from core.enumerations import ConfigDataTypes
from core.enumerations import ConfigDataTypes, ExceptionLevels
from core.enumerations import ConfigFlags
from core.enumerations import ConfigTlvs
from core.enumerations import EventTlvs
@ -551,6 +551,14 @@ class CoreHandler(SocketServer.BaseRequestHandler):
# set initial session state
self.session.set_state(EventTypes.DEFINITION_STATE)
# send service errors, if present
if self.coreemu.service_errors:
self.send_exception(
ExceptionLevels.ERROR,
"ServiceManager.load()",
"Failed to load services: %s" % " ".join(self.coreemu.service_errors)
)
while True:
try:
message = self.receive_message()
@ -579,6 +587,26 @@ class CoreHandler(SocketServer.BaseRequestHandler):
logger.debug("BROADCAST TO OTHER CLIENT: %s", client)
client.sendall(message.raw_message)
def send_exception(self, level, source, text, node=None):
"""
Sends an exception for display within the GUI.
:param core.enumerations.ExceptionLevel level: level for exception
:param str source: source where exception came from
:param str text: details about exception
:param int node: node id, if related to a specific node
:return:
"""
exception_data = ExceptionData(
session=str(self.session.session_id),
node=node,
date=time.ctime(),
level=level.value,
source=source,
text=text
)
self.handle_broadcast_exception(exception_data)
def add_session_handlers(self):
logger.debug("adding session broadcast handlers")
self.session.event_handlers.append(self.handle_broadcast_event)

View file

@ -15,6 +15,7 @@ from core.enumerations import LinkTypes
from core.enumerations import NodeTypes
from core.misc import nodemaps
from core.misc import nodeutils
from core.service import ServiceManager
from core.session import Session
from core.xml.xmlparser import core_document_parser
from core.xml.xmlwriter import core_document_writer
@ -813,12 +814,26 @@ class CoreEmu(object):
node_map = nodemaps.NODES
nodeutils.set_node_map(node_map)
# load default services
core.services.load()
# load services
self.service_errors = []
self.load_services()
# catch exit event
atexit.register(self.shutdown)
def load_services(self):
# load default services
self.service_errors = core.services.load()
# load custom services
service_paths = self.config.get("custom_services_dir")
logger.debug("custom service paths: %s", service_paths)
if service_paths:
for service_path in service_paths.split(','):
service_path = service_path.strip()
custom_service_errors = ServiceManager.add_services(service_path)
self.service_errors.extend(custom_service_errors)
def update_nodes(self, node_map):
"""
Updates node map used by core.

View file

@ -7,6 +7,8 @@ a list of available services to the GUI and for configuring individual
services.
"""
from core.constants import which
from core import CoreCommandError
from core import logger
from core.data import FileData
@ -126,8 +128,18 @@ class ServiceManager(object):
"""
logger.info("loading service: %s", service.__name__)
name = service.name
# avoid duplicate services
if name in cls.services:
raise ValueError("duplicate service being added: %s" % name)
# validate dependent executables are present
for executable in service.executables:
if not which(executable):
logger.error("service(%s) missing executable: %s", service.name, executable)
raise ValueError("service(%s) missing executable: %s" % (service.name, executable))
# make service available
cls.services[name] = service
@classmethod
@ -147,15 +159,22 @@ class ServiceManager(object):
Method for retrieving all CoreServices from a given path.
:param str path: path to retrieve services from
:return: list of core services
:rtype: list
:return: list of core services that failed to load
:rtype: list[str]
"""
service_errors = []
services = utils.load_classes(path, CoreService)
for service in services:
if not service.name:
continue
service.on_load()
cls.add(service)
try:
cls.add(service)
except ValueError as e:
service_errors.append(service.name)
logger.error("failure loading service: %s", e.message)
return service_errors
class CoreServices(object):
@ -623,6 +642,9 @@ class CoreService(object):
# service name should not include spaces
name = None
# executables that must exist for service to run
executables = ()
# group string allows grouping services together
group = None

View file

@ -15,6 +15,7 @@ def load():
"""
Loads all services from the modules that reside under core.services.
:return: nothing
:return: list of services that failed to load
:rtype: list[str]
"""
ServiceManager.add_services(_PATH)
return ServiceManager.add_services(_PATH)

View file

@ -10,6 +10,7 @@ class Bird(CoreService):
Bird router support
"""
name = "bird"
executables = ("bird",)
group = "BIRD"
depends = ()
dirs = ("/etc/bird",)
@ -91,6 +92,7 @@ class BirdService(CoreService):
"""
name = None
executables = ("bird",)
group = "BIRD"
depends = ("bird",)
dirs = ()

View file

@ -113,6 +113,7 @@ class DockerService(CoreService):
node.
"""
name = "Docker"
executables = ("docker",)
group = "Docker"
depends = ()
dirs = ('/var/lib/docker/containers/', '/run/shm', '/run/resolvconf',)

View file

@ -11,14 +11,8 @@ class SdnService(CoreService):
"""
Parent class for SDN services.
"""
name = None
group = "SDN"
depends = ()
dirs = ()
configs = ()
startindex = 50
startup = ()
shutdown = ()
@classmethod
def generateconfig(cls, node, filename, services):
@ -27,6 +21,7 @@ class SdnService(CoreService):
class OvsService(SdnService):
name = "OvsService"
executables = ("ovs-ofctl", "ovs-vsctl")
group = "SDN"
depends = ()
dirs = ("/etc/openvswitch", "/var/run/openvswitch", "/var/log/openvswitch")
@ -101,6 +96,7 @@ class OvsService(SdnService):
class RyuService(SdnService):
name = "ryuService"
executables = ("ryu-manager",)
group = "SDN"
depends = ()
dirs = ()

View file

@ -12,6 +12,7 @@ class XorpRtrmgr(CoreService):
enabled XORP services, and launches necessary daemons upon startup.
"""
name = "xorp_rtrmgr"
executables = ("xorp_rtrmgr",)
group = "XORP"
depends = ()
dirs = ("/etc/xorp",)
@ -75,6 +76,7 @@ class XorpService(CoreService):
common to XORP's routing daemons.
"""
name = None
executables = ("xorp_rtrmgr",)
group = "XORP"
depends = ("xorp_rtrmgr",)
dirs = ()

View file

@ -4,7 +4,6 @@ Sample user-defined service.
from core.misc.ipaddress import Ipv4Prefix
from core.service import CoreService
from core.service import ServiceManager
class MyService(CoreService):
@ -12,22 +11,22 @@ class MyService(CoreService):
This is a sample user-defined service.
"""
# a unique name is required, without spaces
_name = "MyService"
name = "MyService"
# you can create your own group here
_group = "Utility"
group = "Utility"
# list of other services this service depends on
_depends = ()
depends = ()
# per-node directories
_dirs = ()
dirs = ()
# generated files (without a full path this file goes in the node's dir,
# e.g. /tmp/pycore.12345/n1.conf/)
_configs = ('myservice.sh',)
configs = ('myservice.sh',)
# this controls the starting order vs other enabled services
_startindex = 50
startindex = 50
# list of startup commands, also may be generated during startup
_startup = ('sh myservice.sh',)
startup = ('sh myservice.sh',)
# list of shutdown commands
_shutdown = ()
shutdown = ()
@classmethod
def generateconfig(cls, node, filename, services):
@ -57,9 +56,3 @@ class MyService(CoreService):
else:
net = Ipv4Prefix(x)
return 'echo " network %s"' % net
# this is needed to load desired services when being integrated into core, otherwise this is not needed
def load_services():
# this line is required to add the above class to the list of available services
ServiceManager.add(MyService)

View file

@ -16,7 +16,6 @@ from core import logger
from core.corehandlers import CoreHandler
from core.coreserver import CoreServer
from core.misc.utils import close_onexec
from core.service import ServiceManager
def banner():
@ -116,14 +115,6 @@ def main():
for a in args:
logger.error("ignoring command line argument: %s", a)
# attempt load custom services
service_paths = cfg.get("custom_services_dir")
logger.debug("custom service paths: %s", service_paths)
if service_paths:
for service_path in service_paths.split(','):
service_path = service_path.strip()
ServiceManager.add_services(service_path)
banner()
# check if ovs flag was provided