added service executable check, added error message to gui for service load errors
This commit is contained in:
parent
e80736061f
commit
bf47e5fc0d
10 changed files with 90 additions and 39 deletions
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 = ()
|
||||||
|
|
|
@ -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',)
|
||||||
|
|
|
@ -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 = ()
|
||||||
|
|
|
@ -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 = ()
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue