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 coreapi
from core.api import dataconversion from core.api import dataconversion
from core.conf import ConfigShim 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 EventData
from core.data import FileData from core.data import FileData
from core.emulator.emudata import InterfaceData from core.emulator.emudata import InterfaceData
from core.emulator.emudata import LinkOptions from core.emulator.emudata import LinkOptions
from core.emulator.emudata import NodeOptions 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 ConfigFlags
from core.enumerations import ConfigTlvs from core.enumerations import ConfigTlvs
from core.enumerations import EventTlvs from core.enumerations import EventTlvs
@ -551,6 +551,14 @@ class CoreHandler(SocketServer.BaseRequestHandler):
# set initial session state # set initial session state
self.session.set_state(EventTypes.DEFINITION_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: while True:
try: try:
message = self.receive_message() message = self.receive_message()
@ -579,6 +587,26 @@ class CoreHandler(SocketServer.BaseRequestHandler):
logger.debug("BROADCAST TO OTHER CLIENT: %s", client) logger.debug("BROADCAST TO OTHER CLIENT: %s", client)
client.sendall(message.raw_message) 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): def add_session_handlers(self):
logger.debug("adding session broadcast handlers") logger.debug("adding session broadcast handlers")
self.session.event_handlers.append(self.handle_broadcast_event) 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.enumerations import NodeTypes
from core.misc import nodemaps from core.misc import nodemaps
from core.misc import nodeutils from core.misc import nodeutils
from core.service import ServiceManager
from core.session import Session from core.session import Session
from core.xml.xmlparser import core_document_parser from core.xml.xmlparser import core_document_parser
from core.xml.xmlwriter import core_document_writer from core.xml.xmlwriter import core_document_writer
@ -813,12 +814,26 @@ class CoreEmu(object):
node_map = nodemaps.NODES node_map = nodemaps.NODES
nodeutils.set_node_map(node_map) nodeutils.set_node_map(node_map)
# load default services # load services
core.services.load() self.service_errors = []
self.load_services()
# catch exit event # catch exit event
atexit.register(self.shutdown) 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): def update_nodes(self, node_map):
""" """
Updates node map used by core. 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. services.
""" """
from core.constants import which
from core import CoreCommandError from core import CoreCommandError
from core import logger from core import logger
from core.data import FileData from core.data import FileData
@ -126,8 +128,18 @@ class ServiceManager(object):
""" """
logger.info("loading service: %s", service.__name__) logger.info("loading service: %s", service.__name__)
name = service.name name = service.name
# avoid duplicate services
if name in cls.services: if name in cls.services:
raise ValueError("duplicate service being added: %s" % name) raise ValueError("duplicate service being added: %s" % name)
# 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 cls.services[name] = service
@classmethod @classmethod
@ -147,15 +159,22 @@ class ServiceManager(object):
Method for retrieving all CoreServices from a given path. Method for retrieving all CoreServices from a given path.
:param str path: path to retrieve services from :param str path: path to retrieve services from
:return: list of core services :return: list of core services that failed to load
:rtype: list :rtype: list[str]
""" """
service_errors = []
services = utils.load_classes(path, CoreService) services = utils.load_classes(path, CoreService)
for service in services: for service in services:
if not service.name: if not service.name:
continue continue
service.on_load() service.on_load()
cls.add(service)
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): class CoreServices(object):
@ -623,6 +642,9 @@ class CoreService(object):
# service name should not include spaces # service name should not include spaces
name = None name = None
# executables that must exist for service to run
executables = ()
# group string allows grouping services together # group string allows grouping services together
group = None group = None

View file

@ -15,6 +15,7 @@ def load():
""" """
Loads all services from the modules that reside under core.services. 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 Bird router support
""" """
name = "bird" name = "bird"
executables = ("bird",)
group = "BIRD" group = "BIRD"
depends = () depends = ()
dirs = ("/etc/bird",) dirs = ("/etc/bird",)
@ -91,6 +92,7 @@ class BirdService(CoreService):
""" """
name = None name = None
executables = ("bird",)
group = "BIRD" group = "BIRD"
depends = ("bird",) depends = ("bird",)
dirs = () dirs = ()

View file

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

View file

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

View file

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

View file

@ -4,7 +4,6 @@ Sample user-defined service.
from core.misc.ipaddress import Ipv4Prefix from core.misc.ipaddress import Ipv4Prefix
from core.service import CoreService from core.service import CoreService
from core.service import ServiceManager
class MyService(CoreService): class MyService(CoreService):
@ -12,22 +11,22 @@ class MyService(CoreService):
This is a sample user-defined service. This is a sample user-defined service.
""" """
# a unique name is required, without spaces # a unique name is required, without spaces
_name = "MyService" name = "MyService"
# you can create your own group here # you can create your own group here
_group = "Utility" group = "Utility"
# list of other services this service depends on # list of other services this service depends on
_depends = () depends = ()
# per-node directories # per-node directories
_dirs = () dirs = ()
# generated files (without a full path this file goes in the node's dir, # generated files (without a full path this file goes in the node's dir,
# e.g. /tmp/pycore.12345/n1.conf/) # e.g. /tmp/pycore.12345/n1.conf/)
_configs = ('myservice.sh',) configs = ('myservice.sh',)
# this controls the starting order vs other enabled services # this controls the starting order vs other enabled services
_startindex = 50 startindex = 50
# list of startup commands, also may be generated during startup # list of startup commands, also may be generated during startup
_startup = ('sh myservice.sh',) startup = ('sh myservice.sh',)
# list of shutdown commands # list of shutdown commands
_shutdown = () shutdown = ()
@classmethod @classmethod
def generateconfig(cls, node, filename, services): def generateconfig(cls, node, filename, services):
@ -57,9 +56,3 @@ class MyService(CoreService):
else: else:
net = Ipv4Prefix(x) net = Ipv4Prefix(x)
return 'echo " network %s"' % net 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.corehandlers import CoreHandler
from core.coreserver import CoreServer from core.coreserver import CoreServer
from core.misc.utils import close_onexec from core.misc.utils import close_onexec
from core.service import ServiceManager
def banner(): def banner():
@ -116,14 +115,6 @@ def main():
for a in args: for a in args:
logger.error("ignoring command line argument: %s", a) 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() banner()
# check if ovs flag was provided # check if ovs flag was provided