added api2 handler and server

This commit is contained in:
Rod A Santiago 2016-07-26 17:54:11 -07:00
parent e4cd3b064f
commit e9c0730c12

View file

@ -32,7 +32,7 @@ except ImportError:
sys.path.append("/usr/local/lib64/python2.7/site-packages") sys.path.append("/usr/local/lib64/python2.7/site-packages")
from core import pycore from core import pycore
from core.constants import * from core.constants import *
from core.api import coreapi from core.api import coreapi, coreapi2
from core.coreobj import PyCoreNet from core.coreobj import PyCoreNet
from core.misc.utils import hexdump, daemonize, cmdresult, mutedetach, closeonexec from core.misc.utils import hexdump, daemonize, cmdresult, mutedetach, closeonexec
from core.misc.xmlsession import opensessionxml, savesessionxml from core.misc.xmlsession import opensessionxml, savesessionxml
@ -286,6 +286,18 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
try: try:
replies = msghandler(msg) replies = msghandler(msg)
self.dispatchreplies(replies)
except Exception, e:
self.warn("%s: exception while handling msg:\n%s\n%s" %
(threading.currentThread().getName(), msg,
traceback.format_exc()))
# Added to allow the API2 handler to define a different behavior when replying
# to messages from clients
def dispatchreplies(self, reply):
''' Dispatch replies to a handled message.
'''
for reply in replies: for reply in replies:
if self.debug: if self.debug:
msgtype, msgflags, msglen = \ msgtype, msgflags, msglen = \
@ -304,10 +316,6 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
self.sendall(reply) self.sendall(reply)
except Exception, e: except Exception, e:
self.warn("Error sending reply data: %s" % e) self.warn("Error sending reply data: %s" % e)
except Exception, e:
self.warn("%s: exception while handling msg:\n%s\n%s" %
(threading.currentThread().getName(), msg,
traceback.format_exc()))
def handle(self): def handle(self):
''' Handle a new connection request from a client. Dispatch to the ''' Handle a new connection request from a client. Dispatch to the
@ -924,7 +932,9 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
try: try:
self.info("executing '%s'" % ex) self.info("executing '%s'" % ex)
if isinstance(self.server, CoreUdpServer): if isinstance(self.server, CoreUdpServer):
server = self.server.tcpserver server = self.server.mainserver
elif isinstance(self.server, CoreApi2Server):
server = self.server.mainserver
else: else:
server = self.server server = self.server
if msg.flags & coreapi.CORE_API_STR_FLAG: if msg.flags & coreapi.CORE_API_STR_FLAG:
@ -1272,8 +1282,8 @@ class CoreDatagramRequestHandler(CoreRequestHandler):
self.nodestatusreq = {} self.nodestatusreq = {}
self.master = False self.master = False
self.session = None self.session = None
self.verbose = bool(server.tcpserver.cfg['verbose'].lower() == "true") self.verbose = bool(server.mainserver.cfg['verbose'].lower() == "true")
self.debug = bool(server.tcpserver.cfg['debug'].lower() == "true") self.debug = bool(server.mainserver.cfg['debug'].lower() == "true")
SocketServer.BaseRequestHandler.__init__(self, request, SocketServer.BaseRequestHandler.__init__(self, request,
client_address, server) client_address, server)
@ -1325,7 +1335,7 @@ class CoreDatagramRequestHandler(CoreRequestHandler):
#self.info("UDP message has session numbers: %s" % sids) #self.info("UDP message has session numbers: %s" % sids)
if len(sids) > 0: if len(sids) > 0:
for sid in sids: for sid in sids:
sess = self.server.tcpserver.getsession(sessionid=sid, sess = self.server.mainserver.getsession(sessionid=sid,
useexisting=True) useexisting=True)
if sess: if sess:
self.session = sess self.session = sess
@ -1336,7 +1346,7 @@ class CoreDatagramRequestHandler(CoreRequestHandler):
(sid, msg.typestr())) (sid, msg.typestr()))
else: else:
# no session specified, find an existing one # no session specified, find an existing one
sess = self.server.tcpserver.getsession(sessionid=0, sess = self.server.mainserver.getsession(sessionid=0,
useexisting=True) useexisting=True)
if sess or msg.msgtype == coreapi.CORE_API_REG_MSG: if sess or msg.msgtype == coreapi.CORE_API_REG_MSG:
self.session = sess self.session = sess
@ -1360,6 +1370,108 @@ class CoreDatagramRequestHandler(CoreRequestHandler):
class CoreApi2RequestHandler(CoreRequestHandler):
''' A child of the CoreRequestHandler class for handling API 2 specification
messages. **TODO: Verify this statement: 'No new session is created; messages are handled immediately or
sometimes queued on existing session handlers.'
'''
def __init__(self, request, client_address, server):
self.msghandler = {
coreapi.CORE_API_NODE_MSG: self.handlenodemsg,
coreapi.CORE_API_LINK_MSG: self.handlelinkmsg,
coreapi.CORE_API_EXEC_MSG: self.handleexecmsg,
coreapi.CORE_API_REG_MSG: self.handleregmsg,
coreapi.CORE_API_CONF_MSG: self.handleconfmsg,
coreapi.CORE_API_FILE_MSG: self.handlefilemsg,
coreapi.CORE_API_IFACE_MSG: self.handleifacemsg,
coreapi.CORE_API_EVENT_MSG: self.handleeventmsg,
coreapi.CORE_API_SESS_MSG: self.handlesessionmsg,
}
self.nodestatusreq = {}
self.master = False
self.session = None
self.verbose = bool(server.mainserver.cfg['verbose'].lower() == "true")
self.debug = bool(server.mainserver.cfg['debug'].lower() == "true")
SocketServer.BaseRequestHandler.__init__(self, request,
client_address, server)
def setup(self):
''' Client has connected, set up a new connection.
'''
if self.verbose:
self.info("new API 2 connection: %s:%s" % self.client_address)
def handle(self):
port = self.request.getpeername()[1]
self.session = self.server.mainserver.getsession(sessionid = port,
useexisting = False)
self.session.connect(self)
while True:
try:
data = self.recvmsg()
msgs = coreapi2.CoreMessage.toLegacyApi(data)
if msgs:
for msg in msgs:
print msg
self.session.broadcast(self, msg)
self.handlemsg(msg)
except EOFError:
break;
except IOError, e:
self.warn("API2 IOError: %s" % e)
break;
def dispatchreplies(self, replies):
''' Dispatch a reply to a previously received message.
'''
api2Replies = coreapi2.CoreMessage.toApi2(replies)
for reply in api2Replies:
try:
self.sendall(reply)
except Exception, e:
self.warn("Error sending reply data: %s" % e)
def finish(self):
return SocketServer.BaseRequestHandler.finish(self)
def recvmsg(self):
''' Receive data, parse a CoreMessage and queue it onto an existing
session handler's queue, if available.
'''
try:
hdr = self.request.recv(coreapi2.CoreMessage.hdrsiz)
if self.debug and len(msghdr) > 0:
self.info("received message header:\n%s" % hexdump(msghdr))
except Exception, e:
raise IOError, "error receiving API 2 header (%s)" % e
if len(hdr) != coreapi2.CoreMessage.hdrsiz:
if len(hdr) == 0:
raise EOFError, "client disconnected"
else:
print coreapi2.CoreMessage.hdrsiz, len(hdr)
raise IOError, "invalid message header size"
dataToRead = struct.unpack(coreapi2.CoreMessage.hdrfmt, hdr)[0]
data = ""
while len(data) < dataToRead:
data += self.request.recv(dataToRead - len(data))
return data
def queuemsg(self, msg):
raise Exception, "TO BE IMPLEMENTED if needed" % msg.typestr()
class CoreServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): class CoreServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
''' TCP server class, manages sessions and spawns request handlers for ''' TCP server class, manages sessions and spawns request handlers for
incoming connections. incoming connections.
@ -1555,6 +1667,7 @@ class CoreServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
break break
finally: finally:
self._sessionslock.release() self._sessionslock.release()
return found return found
def startudp(self, server_address): def startudp(self, server_address):
@ -1567,6 +1680,23 @@ class CoreServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
self.udpthread.daemon = True self.udpthread.daemon = True
self.udpthread.start() self.udpthread.start()
#
# API 2.0 server
#
def startapi2(self, api2_address):
''' Start a thread running a TCP server on the given address. This
server will communicate with remotes using API 2.0 specification
'''
self.api2server = CoreApi2Server(api2_address,
CoreApi2RequestHandler,
self)
self.api2thread = threading.Thread(target = self.api2server.start)
self.api2thread.daemon = True
self.api2thread.start()
class CoreUdpServer(SocketServer.ThreadingMixIn, SocketServer.UDPServer): class CoreUdpServer(SocketServer.ThreadingMixIn, SocketServer.UDPServer):
''' UDP server class, manages sessions and spawns request handlers for ''' UDP server class, manages sessions and spawns request handlers for
incoming connections. incoming connections.
@ -1574,11 +1704,11 @@ class CoreUdpServer(SocketServer.ThreadingMixIn, SocketServer.UDPServer):
daemon_threads = True daemon_threads = True
allow_reuse_address = True allow_reuse_address = True
def __init__(self, server_address, RequestHandlerClass, tcpserver): def __init__(self, server_address, RequestHandlerClass, mainserver):
''' Server class initialization takes configuration data and calls ''' Server class initialization takes configuration data and calls
the SocketServer constructor the SocketServer constructor
''' '''
self.tcpserver = tcpserver self.mainserver = mainserver # tcpserver is the main server
SocketServer.UDPServer.__init__(self, server_address, SocketServer.UDPServer.__init__(self, server_address,
RequestHandlerClass) RequestHandlerClass)
@ -1589,6 +1719,30 @@ class CoreUdpServer(SocketServer.ThreadingMixIn, SocketServer.UDPServer):
class CoreApi2Server(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
''' TCP server for API version 2.
'''
daemon_threads = True
allow_reuse_address = True
def __init__(self, server_address, RequestHandlerClass, mainserver):
self.mainserver = mainserver
sys.stdout.write("API2 server started, listening on: %s:%s\n" % server_address)
sys.stdout.flush()
SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass)
def start(self):
self.serve_forever()
def setsessionmaster(self, handler):
found = self.mainserver.setsessionmaster(handler)
print "api2 setsessionmaster", found
return found
def tosessionmsg(self, flags = 0):
return self.mainserver.tosessionmsg(flags)
def banner(): def banner():
''' Output the program banner printed to the terminal or log file. ''' Output the program banner printed to the terminal or log file.
''' '''
@ -1616,6 +1770,11 @@ def cored(cfg = None):
sys.stdout.flush() sys.stdout.flush()
server.startudp((host,port)) server.startudp((host,port))
closeonexec(server.udpserver.fileno()) closeonexec(server.udpserver.fileno())
api2port = cfg['api2port']
if api2port:
server.startapi2((host, int(api2port)))
closeonexec(server.api2server.fileno())
server.serve_forever() server.serve_forever()
def cleanup(): def cleanup():
@ -1676,6 +1835,7 @@ def getMergedConfig(filename):
'daemonize' : 'False', 'daemonize' : 'False',
'debug' : 'False', 'debug' : 'False',
'execfile' : None, 'execfile' : None,
'api2port' : None,
} }
usagestr = "usage: %prog [-h] [options] [args]\n\n" + \ usagestr = "usage: %prog [-h] [options] [args]\n\n" + \