added api2 handler and server
This commit is contained in:
parent
e4cd3b064f
commit
e9c0730c12
1 changed files with 186 additions and 26 deletions
|
@ -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,29 +286,37 @@ class CoreRequestHandler(SocketServer.BaseRequestHandler):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
replies = msghandler(msg)
|
replies = msghandler(msg)
|
||||||
for reply in replies:
|
self.dispatchreplies(replies)
|
||||||
if self.debug:
|
|
||||||
msgtype, msgflags, msglen = \
|
|
||||||
coreapi.CoreMessage.unpackhdr(reply)
|
|
||||||
try:
|
|
||||||
rmsg = coreapi.msg_class(msgtype)(msgflags,
|
|
||||||
reply[:coreapi.CoreMessage.hdrsiz],
|
|
||||||
reply[coreapi.CoreMessage.hdrsiz:])
|
|
||||||
except KeyError:
|
|
||||||
# multiple TLVs of same type cause KeyError exception
|
|
||||||
rmsg = "CoreMessage (type %d flags %d length %d)" % \
|
|
||||||
(msgtype, msgflags, msglen)
|
|
||||||
self.info("%s: reply msg:\n%s" %
|
|
||||||
(threading.currentThread().getName(), rmsg))
|
|
||||||
try:
|
|
||||||
self.sendall(reply)
|
|
||||||
except Exception, e:
|
|
||||||
self.warn("Error sending reply data: %s" % e)
|
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.warn("%s: exception while handling msg:\n%s\n%s" %
|
self.warn("%s: exception while handling msg:\n%s\n%s" %
|
||||||
(threading.currentThread().getName(), msg,
|
(threading.currentThread().getName(), msg,
|
||||||
traceback.format_exc()))
|
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:
|
||||||
|
if self.debug:
|
||||||
|
msgtype, msgflags, msglen = \
|
||||||
|
coreapi.CoreMessage.unpackhdr(reply)
|
||||||
|
try:
|
||||||
|
rmsg = coreapi.msg_class(msgtype)(msgflags,
|
||||||
|
reply[:coreapi.CoreMessage.hdrsiz],
|
||||||
|
reply[coreapi.CoreMessage.hdrsiz:])
|
||||||
|
except KeyError:
|
||||||
|
# multiple TLVs of same type cause KeyError exception
|
||||||
|
rmsg = "CoreMessage (type %d flags %d length %d)" % \
|
||||||
|
(msgtype, msgflags, msglen)
|
||||||
|
self.info("%s: reply msg:\n%s" %
|
||||||
|
(threading.currentThread().getName(), rmsg))
|
||||||
|
try:
|
||||||
|
self.sendall(reply)
|
||||||
|
except Exception, e:
|
||||||
|
self.warn("Error sending reply data: %s" % e)
|
||||||
|
|
||||||
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
|
||||||
recvmsg() method for receiving data into CORE API messages, and
|
recvmsg() method for receiving data into CORE API messages, and
|
||||||
|
@ -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" + \
|
||||||
|
|
Loading…
Add table
Reference in a new issue