core-extra/daemon/sbin/core-daemon

320 lines
12 KiB
Text
Raw Normal View History

#!/usr/bin/env python
#
# CORE
# Copyright (c)2010-2013 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# authors: Tom Goff <thomas.goff@boeing.com>
# Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
core-daemon: the CORE daemon is a server process that receives CORE API
messages and instantiates emulated nodes and networks within the kernel. Various
message handlers are defined and some support for sending messages.
'''
import os, optparse, ConfigParser, gc, shlex, socket, shutil
import atexit
import signal
import importlib
try:
from core import pycore
except ImportError:
# hack for Fedora autoconf that uses the following pythondir:
if "/usr/lib/python2.6/site-packages" in sys.path:
sys.path.append("/usr/local/lib/python2.6/site-packages")
if "/usr/lib64/python2.6/site-packages" in sys.path:
sys.path.append("/usr/local/lib64/python2.6/site-packages")
if "/usr/lib/python2.7/site-packages" in sys.path:
sys.path.append("/usr/local/lib/python2.7/site-packages")
if "/usr/lib64/python2.7/site-packages" in sys.path:
sys.path.append("/usr/local/lib64/python2.7/site-packages")
from core import pycore
from core.coreserver import *
from core.constants import *
2016-08-12 19:58:30 +01:00
from core.api import coreapi
from core.misc.utils import daemonize, closeonexec
DEFAULT_MAXFD = 1024
# garbage collection debugging
# gc.set_debug(gc.DEBUG_STATS | gc.DEBUG_LEAK)
coreapi.add_node_class("CORE_NODE_DEF",
coreapi.CORE_NODE_DEF, pycore.nodes.CoreNode)
coreapi.add_node_class("CORE_NODE_PHYS",
coreapi.CORE_NODE_PHYS, pycore.pnodes.PhysicalNode)
try:
coreapi.add_node_class("CORE_NODE_XEN",
coreapi.CORE_NODE_XEN, pycore.xen.XenNode)
except Exception:
#print "XenNode class unavailable."
pass
coreapi.add_node_class("CORE_NODE_TBD",
coreapi.CORE_NODE_TBD, None)
coreapi.add_node_class("CORE_NODE_SWITCH",
coreapi.CORE_NODE_SWITCH, pycore.nodes.SwitchNode)
coreapi.add_node_class("CORE_NODE_HUB",
coreapi.CORE_NODE_HUB, pycore.nodes.HubNode)
coreapi.add_node_class("CORE_NODE_WLAN",
coreapi.CORE_NODE_WLAN, pycore.nodes.WlanNode)
coreapi.add_node_class("CORE_NODE_RJ45",
coreapi.CORE_NODE_RJ45, pycore.nodes.RJ45Node)
coreapi.add_node_class("CORE_NODE_TUNNEL",
coreapi.CORE_NODE_TUNNEL, pycore.nodes.TunnelNode)
coreapi.add_node_class("CORE_NODE_EMANE",
coreapi.CORE_NODE_EMANE, pycore.nodes.EmaneNode)
2016-07-27 01:54:11 +01:00
def startudp(mainserver, server_address):
''' Start a thread running a UDP server on the same host,port for
connectionless requests.
'''
mainserver.udpserver = CoreUdpServer(server_address,
CoreDatagramRequestHandler, mainserver)
mainserver.udpthread = threading.Thread(target = mainserver.udpserver.start)
mainserver.udpthread.daemon = True
mainserver.udpthread.start()
return mainserver.udpserver
#
# Auxiliary server startup
#
def startaux(mainserver, aux_address, aux_handler):
''' Start a thread running an auxiliary TCP server on the given address.
This server will communicate with client requests using a handler
using the aux_handler class. The aux_handler can provide an alternative
API to CORE.
2016-07-27 01:54:11 +01:00
'''
handlermodname,dot,handlerclassname = aux_handler.rpartition('.')
handlermod = importlib.import_module(handlermodname)
handlerclass = getattr(handlermod, handlerclassname)
mainserver.auxserver = CoreAuxServer(aux_address,
handlerclass,
mainserver)
mainserver.auxthread = threading.Thread(target = mainserver.auxserver.start)
mainserver.auxthread.daemon = True
mainserver.auxthread.start()
return mainserver.auxserver
2016-07-27 01:54:11 +01:00
def banner():
''' Output the program banner printed to the terminal or log file.
'''
sys.stdout.write("CORE daemon v.%s started %s\n" % \
(COREDPY_VERSION, time.ctime()))
sys.stdout.flush()
def cored(cfg = None):
''' Start the CoreServer object and enter the server loop.
'''
host = cfg['listenaddr']
port = int(cfg['port'])
if host == '' or host is None:
host = "localhost"
try:
server = CoreServer((host, port), CoreRequestHandler, cfg)
except Exception, e:
sys.stderr.write("error starting server on: %s:%s\n\t%s\n" % \
(host, port, e))
sys.stderr.flush()
sys.exit(1)
closeonexec(server.fileno())
2016-09-06 23:48:41 +01:00
sys.stdout.write("main server started, listening on: %s:%s\n" % (host, port))
sys.stdout.flush()
udpserver = startudp(server, (host,port))
closeonexec(udpserver.fileno())
auxreqhandler = cfg['aux_request_handler']
if auxreqhandler:
try:
handler, auxport = auxreqhandler.rsplit(':')
auxserver = startaux(server, (host,int(auxport)), handler)
closeonexec(auxserver.fileno())
except Exception as e:
raise ValueError, "invalid auxreqhandler:(%s)\nError: %s" % (auxreqhandler, e)
2016-07-27 01:54:11 +01:00
server.serve_forever()
def cleanup():
while CoreServer.servers:
server = CoreServer.servers.pop()
server.shutdown()
atexit.register(cleanup)
def sighandler(signum, stackframe):
print >> sys.stderr, "terminated by signal:", signum
sys.exit(signum)
signal.signal(signal.SIGHUP, sighandler)
signal.signal(signal.SIGINT, sighandler)
signal.signal(signal.SIGPIPE, sighandler)
signal.signal(signal.SIGTERM, sighandler)
signal.signal(signal.SIGUSR1, sighandler)
signal.signal(signal.SIGUSR2, sighandler)
def logrotate(stdout, stderr, stdoutmode = 0644, stderrmode = 0644):
def reopen(fileno, filename, mode):
err = 0
fd = -1
try:
fd = os.open(filename,
os.O_WRONLY | os.O_CREAT | os.O_APPEND, mode)
os.dup2(fd, fileno)
except OSError as e:
err = e.errno
finally:
if fd >= 0:
os.close(fd)
return err
if stdout:
err = reopen(1, stdout, stdoutmode)
if stderr:
if stderr == stdout and not err:
try:
os.dup2(1, 2)
except OSError as e:
pass
else:
reopen(2, stderr, stderrmode)
def getMergedConfig(filename):
''' Return a configuration after merging config file and command-line
arguments.
'''
# these are the defaults used in the config file
defaults = { 'port' : '%d' % coreapi.CORE_API_PORT,
'listenaddr' : 'localhost',
'pidfile' : '%s/run/core-daemon.pid' % CORE_STATE_DIR,
'logfile' : '%s/log/core-daemon.log' % CORE_STATE_DIR,
'xmlfilever' : '1.0',
'numthreads' : '1',
'verbose' : 'False',
'daemonize' : 'False',
'debug' : 'False',
'execfile' : None,
'aux_request_handler' : None
}
usagestr = "usage: %prog [-h] [options] [args]\n\n" + \
"CORE daemon v.%s instantiates Linux network namespace " \
"nodes." % COREDPY_VERSION
parser = optparse.OptionParser(usage = usagestr)
parser.add_option("-f", "--configfile", dest = "configfile",
type = "string",
help = "read config from specified file; default = %s" %
filename)
parser.add_option("-d", "--daemonize", dest = "daemonize",
action="store_true",
help = "run in background as daemon; default=%s" % \
defaults["daemonize"])
parser.add_option("-e", "--execute", dest = "execfile", type = "string",
help = "execute a Python/XML-based session")
parser.add_option("-l", "--logfile", dest = "logfile", type = "string",
help = "log output to specified file; default = %s" %
defaults["logfile"])
parser.add_option("-p", "--port", dest = "port", type = int,
help = "port number to listen on; default = %s" % \
defaults["port"])
parser.add_option("-i", "--pidfile", dest = "pidfile",
help = "filename to write pid to; default = %s" % \
defaults["pidfile"])
parser.add_option("-t", "--numthreads", dest = "numthreads", type = int,
help = "number of server threads; default = %s" % \
defaults["numthreads"])
parser.add_option("-v", "--verbose", dest = "verbose", action="store_true",
help = "enable verbose logging; default = %s" % \
defaults["verbose"])
parser.add_option("-g", "--debug", dest = "debug", action="store_true",
help = "enable debug logging; default = %s" % \
defaults["debug"])
# parse command line options
(options, args) = parser.parse_args()
# read the config file
if options.configfile is not None:
filename = options.configfile
del options.configfile
cfg = ConfigParser.SafeConfigParser(defaults)
cfg.read(filename)
section = "core-daemon"
if not cfg.has_section(section):
cfg.add_section(section)
# gracefully support legacy configs (cored.py/cored now core-daemon)
if cfg.has_section("cored.py"):
for name, val in cfg.items("cored.py"):
if name == 'pidfile' or name == 'logfile':
bn = os.path.basename(val).replace('coredpy', 'core-daemon')
val = os.path.join(os.path.dirname(val), bn)
cfg.set(section, name, val)
if cfg.has_section("cored"):
for name, val in cfg.items("cored"):
if name == 'pidfile' or name == 'logfile':
bn = os.path.basename(val).replace('cored', 'core-daemon')
val = os.path.join(os.path.dirname(val), bn)
cfg.set(section, name, val)
# merge command line with config file
for opt in options.__dict__:
val = options.__dict__[opt]
if val is not None:
cfg.set(section, opt, val.__str__())
return dict(cfg.items(section)), args
def exec_file(cfg):
''' Send a Register Message to execute a new session based on XML or Python
script file.
'''
filename = cfg['execfile']
sys.stdout.write("Telling daemon to execute file: '%s'...\n" % filename)
sys.stdout.flush()
tlvdata = coreapi.CoreRegTlv.pack(coreapi.CORE_TLV_REG_EXECSRV, filename)
msg = coreapi.CoreRegMessage.pack(coreapi.CORE_API_ADD_FLAG, tlvdata)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.connect(("localhost", int(cfg['port']))) # TODO: connect address option
sock.sendall(msg)
return 0
def main():
''' Main program startup.
'''
# get a configuration merged from config file and command-line arguments
cfg, args = getMergedConfig("%s/core.conf" % CORE_CONF_DIR)
for a in args:
sys.stderr.write("ignoring command line argument: '%s'\n" % a)
if cfg['daemonize'] == 'True':
daemonize(rootdir = None, umask = 0, close_fds = False,
stdin = os.devnull,
stdout = cfg['logfile'], stderr = cfg['logfile'],
pidfilename = cfg['pidfile'],
defaultmaxfd = DEFAULT_MAXFD)
signal.signal(signal.SIGUSR1, lambda signum, stackframe:
logrotate(stdout = cfg['logfile'],
stderr = cfg['logfile']))
banner()
if cfg['execfile']:
cfg['execfile'] = os.path.abspath(cfg['execfile'])
sys.exit(exec_file(cfg))
try:
cored(cfg)
except KeyboardInterrupt:
pass
sys.exit(0)
if __name__ == "__main__":
main()