from twisted.application import internet, service
from twisted.internet import protocol, reactor
from twisted.python import log, usage
from datetime import datetime
import sys
import buffer
class ModuleInfo (object) :
"""
Some info about a module
"""
# module's name
name = None
# module's version, as a 16-bit integer
version = None
# list of valid event types (strings)
event_types = None
def __str__ (self) :
return "Module %s:%d" % (self.name, self.version)
def __repr__ (self) :
return "<module %s:%d with events: %s>" % (self.name, self.version, ", ".join(self.event_types))
class Event (object) :
# the ModuleInfo object
module = None
# the event type as a string
type = None
# event message as a string (under 255 bytes in length!)
msg = None
# timestamp as a datetime.datetime
when = None
def __init__ (self, module, type, msg) :
assert type in module.event_types, "Invalid event-type %s for %r" % (type, self.module)
self.module = module
self.type = type
self.msg = msg
self.when = datetime.now()
def __str__ (self) :
return "[%s] %s" % (self.type, self.msg)
def __repr__ (self) :
return "%s @ %s" % (self.type, self.when)
CLIENT_COMMANDS = [
"module_init",
"module_event",
]
SERVER_COMMANDS = [
"module_ok",
]
class ServerProtocol (buffer.StreamProtocol, protocol.Protocol) :
RECV_COMMANDS = CLIENT_COMMANDS
SEND_COMMANDS = SERVER_COMMANDS
VALID_STATES = [
"wait_init",
"wait_event"
]
# proto state
state = None
# module info
module = None
def _assert (self, condition, msg) :
if not condition :
self.transport.loseConnection()
log.err("assert failed in APIProtocol for %s: %s" % (self.module, msg))
def connectionMade (self) :
log.msg("Client connected")
def connectionLost (self, reason) :
log.msg("Connection lost: %s" % reason)
if self.module :
self.factory.nexus.unregisterModule(self.module, reason.getErrorMessage())
def on_module_init (self, i) :
self._assert(not self.module, "module_init with non-None self.module")
peer_secret = i.readVarLen('B')
self._assert(peer_secret == self.factory.secret, "Mismatching API secrets!")
m = ModuleInfo()
m.name = i.readVarLen('B')
m.version = i.readItem('H')
m.event_types = list(buffer.readStringStream(i, 'B'))
m.addr = self.transport.getPeer()
self.module_name = m.name
log.msg("Got mod_init for %r" % m)
self.factory.nexus.registerModule(m, self)
self.module = m
o = self.startCommand('module_ok')
self.send(o)
def on_module_event (self, i) :
self._assert(self.module, "module_event with None self.module!")
event_type = i.readEnum(self.module.event_types)
event_msg = i.readVarLen('H')
e = Event(self.module, event_type, event_msg)
# log.msg("Got mod_event of %r" % (e))
self.factory.nexus.handleEvent(e)
def logPrefix (self) :
if self.module :
return str(self.module)
else :
return super(ServerProtocol, self).logPrefix()
class ClientProtocol (buffer.StreamProtocol, protocol.Protocol) :
RECV_COMMANDS = SERVER_COMMANDS
SEND_COMMANDS = CLIENT_COMMANDS
def connectionMade (self) :
log.msg("Connected to API server, sending module init message")
o = self.startCommand('module_init')
o.writeVarLen('B', self.factory.secret)
o.writeVarLen('B', self.factory.name)
o.writeItem("H", self.factory.version)
buffer.writeStringStream(o, 'B', self.factory.event_types)
self.send(o)
def sendEvent (self, event) :
o = self.startCommand('module_event')
o.writeEnum(self.factory.event_types, event.type)
o.writeVarLen('H', event.msg[:2**16])
self.send(o)
def on_module_ok (self, i) :
log.msg("Registration OK")
self.factory.connected(self)
def logPrefix (self) :
return "module %s:%d client" % (self.factory.name, self.factory.version)
class Module (ModuleInfo, protocol.ClientFactory) :
protocol = ClientProtocol
def __init__ (self, secret) :
self.connection = None
self.secret = secret
def connected (self, connection) :
log.msg("Connected!")
self.connection = connection
self.handleConnect()
def disconnect (self) :
self.connection.transport.loseConnection()
def sendEvent (self, type, msg) :
self.connection.sendEvent(self.buildEvent(type, msg))
def buildEvent (self, type, msg) :
return Event(self, type, msg)
def handleConnect (self) :
"""
Do something
"""
pass
class ServerFactory (protocol.ServerFactory) :
protocol = ServerProtocol
def __init__ (self, nexus, secret) :
self.nexus = nexus
self.secret = secret
class ClientOptions (usage.Options) :
optParameters = [
( "api-server", "s", "127.0.0.1", "address of API server to connect to" ),
( "api-port", "P", 34888, "port of API server to connect to", int ),
( "api-secret", None, None, "secret key for API connections" ),
]
optFlags = [
]
def makeService (client_module_factory, config) :
s = service.MultiService()
# the API client
module_factory = client_module_factory(config['api-secret'])
log.msg("Connecting to API server on [%s:%d]" % (config['api-server'], config['api-port']))
api_client = internet.TCPClient(config['api-server'], config['api-port'], module_factory)
api_client.setServiceParent(s)
return s