def write_message_to_other_limpet(self, msg): """Write a Message to the other Limpet. """ # We know enough to sort out the network order of the integers in # the Replier Bind Event's data if msg.name == '$.KBUS.ReplierBindEvent': data = convert_ReplierBindEvent_data_to_network(msg.data) msg = Message.from_message(msg, data=data) header = serialise_message_header(msg) self.sock.sendall(header) self.sock.sendall(msg.name) padded_name_len = calc_padded_name_len(msg.msg.name_len) if len(msg.name) != padded_name_len: self.sock.sendall('\0'*(padded_name_len - len(msg.name))) if msg.msg.data_len: self.sock.sendall(msg.data) padded_data_len = calc_padded_data_len(msg.msg.data_len) if len(msg.data) != padded_data_len: self.sock.sendall('\0'*(padded_data_len - len(msg.data))) ##end_guard = struct.pack('!L', header[-1]) end_guard = struct.pack('!L', Message.END_GUARD) self.sock.sendall(end_guard) # end guard again
def read_message_from_other_limpet(self): """Read a message from the other Limpet. Returns the corresponding Message instance. """ # First, read the message header header = self.sock.recv(_SERIALISED_MESSAGE_HEADER_LEN * 4, socket.MSG_WAITALL) if header == '': raise OtherLimpetGoneAway() name_len, data_len, array = unserialise_message_header(header) if array[0] != Message.START_GUARD: raise BadMessage('Message data start guard is %08x,' ' not %08x' % (array[0], Message.START_GUARD)) if array[-1] != Message.END_GUARD: raise BadMessage('Message data end guard is %08x,' ' not %08x' % (array[-1], Message.END_GUARD)) name = self.sock.recv(calc_padded_name_len(name_len), socket.MSG_WAITALL) if name == '': raise OtherLimpetGoneAway() if data_len: data = self.sock.recv(calc_padded_data_len(data_len), socket.MSG_WAITALL) if data == '': raise OtherLimpetGoneAway() else: data = None end = self.sock.recv(4, socket.MSG_WAITALL) #value = struct.unpack('!L', end) # unsigned long, network order #end = ntohl(value[0]) end = struct.unpack('!L', end)[0] # unsigned long, network order if end != Message.END_GUARD: raise BadMessage('Final message data end guard is %08x,' ' not %08x' % (end, Message.END_GUARD)) # We know enough to sort out the network order of the integers in # the Replier Bind Event's data if name[:name_len] == '$.KBUS.ReplierBindEvent': data = convert_ReplierBindEvent_data_from_network(data, data_len) return Message(name[:name_len], data=data[:data_len] if data else None, id=MessageId(array[1], array[2]), in_reply_to=MessageId(array[3], array[4]), to=array[5], from_=array[6], orig_from=OrigFrom(array[7], array[8]), final_to=OrigFrom(array[9], array[10]), flags=array[12])
def run_server(listen_address): """Listen for connections, and deal with them. """ keep_listening = True print 'Listening on', listen_address sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) # Try to allow address reuse as soon as possible after we've finished # with it sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(listen_address) try: while keep_listening: sock.listen(1) connection, address = sock.accept() print 'Connected to by (%s, %s)' % (connection, address) generator = message_generator(connection) for msg in generator: if msg is None: keep_listening = False break else: print 'Received: Message %s %s from %s = %s' % ( msg.id, msg.name, msg.from_, msg.data) connection.sendall( Message('$.Test.Result', 'OK').to_string()) print 'Closing connection' connection.close() except BadMessage as err: print err connection.sendall(Message('$.Test.Result', str(err)).to_string()) finally: print 'Closing server' sock.close() remove_socket_file(listen_address)
def teardown_module(): with Ksock(1, 'rw') as sender: sender.send_msg(Message(TERMINATION_MESSAGE)) print 'Limpet termination message sent' g_server.join() g_client.join() print 'Limpet server and client both finished' retcode = system('sudo rmmod kbus') assert retcode == 0 # Via the magic of hotplugging, that should cause our device to go away # ...eventually time.sleep(1) assert not os.path.exists("/dev/kbus0")
def test_the_first(self): """An establishing test, to check we can send a single message. """ with Ksock(KBUS_SENDER, 'rw') as sender: with Ksock(KBUS_LISTENER, 'rw') as listener: listener.bind('$.Fred') this = Message('$.Fred') this_id = sender.send_msg(this) print 'Sent', this # Wait for the message to penetrate the labyrinth that = listener.wait_for_msg(TIMEOUT) print 'Read', that assert this.equivalent(that) assert this_id.serial_num == that.id.serial_num
def test_simple_listening(self): """Test simple listening. """ with Ksock(KBUS_LISTENER, 'rw') as this: with Ksock(KBUS_SENDER, 'rw') as that: print 'this', str(this) print 'that', str(that) # that is listening for someone to say 'Hello' that.bind('$.Hello') # this says 'Hello' to anyone listening m = Message('$.Hello', 'dada') this.send_msg(m) # We read the next message - it's a 'Hello' r = that.wait_for_msg() assert r.name == '$.Hello' print r
def test_reply_to_specific_id(self): """Test replying to a specific id. """ with Ksock(KBUS_LISTENER, 'rw') as replier: with Ksock(KBUS_SENDER, 'rw') as sender: with Ksock(KBUS_LISTENER, 'rw') as listener: print 'Participants:' print ' sender ', str(sender) print ' replier ', str(replier) print ' listener', str(listener) sender.bind('$.KBUS.ReplierBindEvent') # sender is listening for someone to say 'Hello' sender.bind('$.Hello') # replier says 'Hello' to anyone listening m = Message('$.Hello', 'dada') replier.send_msg(m) # We read the next message - it's a 'Hello' r = sender.wait_for_msg() assert r.name == '$.Hello' print r # Two interfaces decide to listen to '$.Reponse' replier.bind('$.Response', True) listener.bind('$.Response') # Synchronise... b = sender.wait_for_msg() assert b.name == '$.KBUS.ReplierBindEvent' # However, sender *cares* that replier should receive its # response, and is not worried about anyone else # doing so. First it needs to get the contact information # for replier. The normal way to do that is to send a # request (this makes sense as "the normal way", since by # definition a stateful request wants a Replier at the # other end, and doing the Request/Reply thing establishes # that that is what we have). req = Request('$.Response') sender.send_msg(req) # The listener gets a plain request a = listener.wait_for_msg() assert not r.wants_us_to_reply() # The replier gets the request-for-reply b = replier.wait_for_msg() assert b.wants_us_to_reply() # and should thus reply to it r = reply_to(b) replier.send_msg(r) # listener receives the reply, because it has the same name c = listener.wait_for_msg() assert c.is_reply() # sender receives the reply m = sender.wait_for_msg() print print '*' * 60 print 'STATEFUL REQUEST' print 'Sender received ', str(m) # and uses *that* to construct a stateful request s = stateful_request(m, '$.Response', 'Aha!') print 'Sender requests ', str(s) sender.send_msg(s) print '*' * 60 # Both recipients should "see" that stateful request r = replier.wait_for_msg() print 'Replier receives ', str(r) assert r.wants_us_to_reply() assert r.to == replier.ksock_id() assert r.data == 'Aha!' l = listener.wait_for_msg() print 'Listener receives', str(l) assert not l.wants_us_to_reply() assert l.to == replier.ksock_id() assert l.data == 'Aha!' assert l.id == r.id # But if replier should stop listening to the responses # (either because it "goes away", or because it unbinds) # then we want to know about this... replier.unbind('$.Response', True) # But the Limpet's have to commnicate that fact. So the # sender has no way of knowing (until that has happened) # that it can't still connect to the same replier... sent_id = sender.send_msg(s) m = sender.wait_for_msg() print 'Replier unbind event', str(m) assert m.name == '$.KBUS.ReplierBindEvent' m = sender.wait_for_msg() print 'Expect complaint that Replier did go away', str(m) assert m.to == sender.ksock_id() assert m.in_reply_to == sent_id # And if someone different starts to reply, we want to # know about that as well listener.bind('$.Response', True) # Synchronise... b = sender.wait_for_msg() print 'Expect new binder', str(b) assert b.name == '$.KBUS.ReplierBindEvent' # And sending our Request again should fail # because it's the wrong replier sent_id = sender.send_msg(s) m = sender.wait_for_msg(5) print 'Finally, got', str(m) assert m.name == '$.KBUS.Replier.NotSameKsock'
def _amend_request_from_socket(self, hdr, msg): """Do whatever is necessary to a Stateful Request from the other Limpet. Returns the amended message, or raises ErrorMessage(<error message>), where <error message> is appropriate for sending to the other Limpet to report the problem. """ # The Request will have been marked "to" our Limpet pair # (otherwise we would not have received it). # If the 'final_to' has a network id that matches ours, # then we need to unset that, as it has clearly now come # into its "local" network. if self.verbosity > 1: print '%s *** final_to.network_id %u, network_id %u' % ( hdr, msg._final_to.network_id, self.network_id) if msg._final_to.network_id == self.network_id: msg._final_to.network_id = 0 # XXX Do we need to do this? is_local = True else: is_local = False # Find out who KBUS thinks is replying to this message name replier_id = self.find_replier(msg.name) if replier_id is None: # Oh dear - there is no replier if self.verbosity > 1: print '%s *** There is no Replier - Replier gone away' % hdr error = Message('$.KBUS.Replier.GoneAway', to=msg.from_, in_reply_to=msg.id) raise ErrorMessage(error) if self.verbosity > 1: print '%s *** %s, kbus replier %u' % (hdr, 'Local' if is_local else 'Nonlocal', replier_id) if is_local: # The KBUS we're going to write the message to is # the final KBUS. Thus the replier id must match # that of the original Replier if replier_id != msg._final_to.local_id: # Oops - wrong replier - someone rebound if self.verbosity > 1: print '%s *** Replier is %u, wanted %u - ' \ 'Replier gone away'%(hdr,replier_id,msg._final_to.local_id) error = Message( '$.KBUS.Replier.NotSameKsock', # XXX New message name to=msg.from_, in_reply_to=msg.id) raise ErrorMessage(error) # Regardless, we believe the message is OK, so need to # adjust who it is meant to go to (locally) if is_local: # If we're in our final stage, then we insist that the # Replier we deliver to be the Replier we expected msg.msg.to = msg._final_to.local_id else: # If we're just passing through, then just deliver it to # whoever is listening, on the assumption that they in turn # will pass it along, until it reaches its destination. # XXX What happens if they are not a Limpet? # XXX That would be bad - but I'm not sure how we could # XXX tell (short of allowing Limpets to register with # XXX KBUS, so that we can ask - and then a non-Limpet # XXX could presumably *pretend* to be a Limpet anyway) # XXX - a potentially infinite shell game then ensues... msg.msg.to = replier_id if self.verbosity > 1: print '%s Adjusted the msg.to field' % hdr return msg