Beispiel #1
0
    def reply(self, message, content, subject=None):
        """
        Replies to a message

        :param message: Original message
        :param content: Content of the response
        :param subject: Reply message subject (same as request if None)
        :raise NoTransport: No transport/access found to send the reply
        """
        # Normalize subject. By default, add a 'reply' prefix,
        # to avoid potential loops
        if not subject:
            subject = '/'.join(('reply', message.subject))

        try:
            # Try to reuse the same transport
            self._fire_reply(beans.Message(subject, content), message)
        except NoTransport:
            # Continue...
            pass
        else:
            # No error
            return

        # If not possible: fire a standard reply
        try:
            # Fire the reply
            self.fire(message.sender, beans.Message(subject, content))
        except KeyError:
            # Convert KeyError to NoTransport
            raise NoTransport(beans.Target(uid=message.sender),
                              "No access to reply to {0}"
                              .format(message.sender))
    def __discover_forker(self):
        """
        Discover the forker of this local Peer by sending to him 
        the contact message
        """
        host = self._forker_host
        port = self._forker_port
        path = self._forker_path

        if path.startswith('/'):
            # Remove the starting /, as it is added while forging the URL
            path = path[1:]

        # Normalize the address of the sender
        host = utils.normalize_ip(host)

        # Prepare the "extra" information, like for a reply
        extra = {'host': host, 'port': port, 'path': path}
        local_dump = self._directory.get_local_peer().dump()
        try:
            self._transport.fire(
                None,
                beans.Message(peer_contact.SUBJECT_DISCOVERY_STEP_1,
                              local_dump), extra)
        except Exception as ex:
            _logger.exception("Error contacting peer: %s", ex)
    def __discover_neighbor(self, peer_dump):
        """
        Discover local neighbor Peer
        """
        host = peer_dump['accesses']['http'][0]
        port = peer_dump['accesses']['http'][1]
        path = peer_dump['accesses']['http'][2]

        if path.startswith('/'):
            # Remove the starting /, as it is added while forging the URL
            path = path[1:]

        # Normalize the address of the sender
        host = utils.normalize_ip(host)

        # Prepare the "extra" information, like for a reply
        extra = {'host': host, 'port': port, 'path': path}
        local_dump = self._directory.get_local_peer().dump()
        try:
            self._transport.fire(
                None,
                beans.Message(peer_contact.SUBJECT_DISCOVERY_STEP_1,
                              local_dump), extra)
        except Exception as ex:
            _logger.exception("Error contacting peer: %s", ex)
Beispiel #4
0
    def __on_ready(self, joined, erroneous):
        """
        Called when all MUC rooms have created or joined

        :param joined: List of joined rooms
        :param erroneous: List of room that couldn't be joined
        """
        _logger.debug("Client joined rooms: %s", ", ".join(joined))
        if erroneous:
            _logger.error("Error joining rooms: %s", ", ".join(erroneous))

        # Register our local access
        local_peer = self._directory.get_local_peer()
        local_peer.set_access(self._access_id,
                              XMPPAccess(self._bot.boundjid.full))

        # Listen to peers going away from the main room
        self._bot.add_event_handler(
            "muc::{0}::got_offline".format(self.room_jid(local_peer.app_id)),
            self._on_room_out)

        if self._bot_lock:
            # We're on line, register our service
            _logger.debug("XMPP transport service activating...")
            self._controller = True
            _logger.info("XMPP transport service activated")
            self._bot_state = "created"
            _logger.info("changing XMPP bot state to : created")

        # Start the discovery handshake, with everybody
        _logger.debug("Sending discovery step 1...")
        message = beans.Message(peer_contact.SUBJECT_DISCOVERY_STEP_1,
                                local_peer.dump())
        self.__send_message("groupchat", self.room_jid(local_peer.app_id),
                            message)
Beispiel #5
0
    def post_group(self, io_handler, group, subject, *words):
        """
        Post a message to the given group of peers
        """
        def callback(_, message):
            """
            Received a reply
            """
            io_handler.write_line("Got answer to {0} from {1}:\n{2}",
                                  message.reply_to, message.sender,
                                  message.content)

        def errback(_, exception):
            """
            Error during message transmission
            """
            io_handler.write_line("Error posting message: {0} ({1})",
                                  type(exception).__name__, exception)

        try:
            uid = self._herald.post_group(
                group, beans.Message(subject, ' '.join(words)),
                callback, errback)
        except KeyError:
            io_handler.write_line("Unknown group: {0}", group)
        except NoTransport:
            io_handler.write_line("No transport to join {0}", group)
        else:
            io_handler.write_line("Message sent: {0}", uid)
    def __discover_peer(self, host, port, path):
        """
        Grabs the description of a peer using the Herald servlet

        :param host: Address which sent the heart beat
        :param port: Port of the Herald HTTP server
        :param path: Path to the Herald HTTP servlet
        """
        if path.startswith('/'):
            # Remove the starting /, as it is added while forging the URL
            path = path[1:]

        # Normalize the address of the sender
        host = utils.normalize_ip(host)

        # Prepare the "extra" information, like for a reply
        extra = {'host': host, 'port': port, 'path': path}
        local_dump = self._directory.get_local_peer().dump()
        try:
            self._transport.fire(
                None,
                beans.Message(peer_contact.SUBJECT_DISCOVERY_STEP_1,
                              local_dump), extra)
        except Exception as ex:
            _logger.exception("Error contacting peer: %s", ex)
Beispiel #7
0
    def __send_message(self, kind, content):
        """
        Fires a discovery message to all peers

        :param kind: Kind of discovery message
        :param content: Content of the message
        """
        self._herald.fire_group('all', beans.Message(self.__subject(kind),
                                                     content))
Beispiel #8
0
 def _get_isolate_directory(self, uuid):
     lp = self._directory.get_local_peer()
     if lp.uid != uuid:
         # this is another isolate
         msg = beans.Message(debug.agent.SUBJECT_GET_ISOLATE_DIRECTORY)
         reply = self._herald.send(uuid, msg)
         return reply.content
     else:
         # this is the local isolate
         return self._agent.get_isolate_directory()
Beispiel #9
0
    def peer_registered(self, peer):
        """
        A new peer has been registered in Herald: send it a contact information

        :param peer: The new peer
        """
        # Send a contact message, with our list of endpoints
        endpoints = self._dump_endpoints(self._dispatcher.get_endpoints())
        self._herald.fire(peer,
                          beans.Message(self.__subject('contact'), endpoints))
    def __send_message(self, kind, content, group=DEFAULT_TARGET_GROUP):
        """
        Fires a discovery message to all peers

        :param kind: Kind of discovery message
        :param content: Content of the message
        :param group: Name of the group to whom the message is to be sent
        """
        self._herald.fire_group(group,
                                beans.Message(self.__subject(kind), content))
Beispiel #11
0
 def _get_isolate_accesses(self, uuid):
     lp = self._directory.get_local_peer()
     if lp.uid != uuid:
         # this is another isolate
         msg = beans.Message(debug.agent.SUBJECT_GET_ISOLATE_ACCESSES)
         reply = self._herald.send(uuid, msg)
         return json.loads(reply.content)
     else:
         # this is the local isolate
         return json.loads(self._agent.get_isolate_accesses())
Beispiel #12
0
 def _get_instance_detail(self, uuid, instance_name):
     lp = self._directory.get_local_peer()
     if lp.uid != uuid:
         # this is another isolate
         msg = beans.Message(debug.agent.SUBJECT_GET_INSTANCE_DETAIL,
                             instance_name)
         reply = self._herald.send(uuid, msg)
         return json.loads(reply.content)
     else:
         # this is the local isolate
         return json.loads(self._agent.get_instance_detail(instance_name))
Beispiel #13
0
 def _get_bundle_detail(self, uuid, bundle_id):
     lp = self._directory.get_local_peer()
     if lp.uid != uuid:
         # this is another isolate
         msg = beans.Message(debug.agent.SUBJECT_GET_BUNDLE_DETAIL,
                             bundle_id)
         reply = self._herald.send(uuid, msg)
         return json.loads(reply.content)
     else:
         # this is the local isolate
         return json.loads(self._agent.get_bundle_detail(bundle_id))
Beispiel #14
0
    def flush(self):
        """
        Sends buffered data to the target
        """
        # Flush buffer
        line = self._buffer.getvalue()
        self._buffer = StringIO()

        # Send the message
        content = {"session_id": self._session, "text": line}
        self._herald.fire(self._peer, beans.Message(MSG_CLIENT_PRINT, content))
Beispiel #15
0
    def stop(self):
        """
        Stops the whole isolate
        """
        # Send the "isolate stopping" signal
        _logger.warning(">>> Isolate will stop <<<")
        message = beans.Message(cohorte.monitor.SIGNAL_ISOLATE_STOPPING,
                                self._context.get_property(cohorte.PROP_UID))
        self._sender.fire_group('all', message)

        _logger.warning(">>> STOPPING isolate <<<")
        self._context.get_bundle(0).stop()
Beispiel #16
0
    def readline(self):
        """
        Waits for a line from the Herald client
        """
        content = {"session_id": self._session}
        prompt_msg = self._herald.send(
            self._peer, beans.Message(MSG_CLIENT_PROMPT, content))
        if prompt_msg.subject == MSG_SERVER_CLOSE:
            # Client closed its shell
            raise EOFError

        return prompt_msg.content
Beispiel #17
0
    def stop(self, uid):
        """
        Stops the given isolate

        :param uid: The UID if an isolate
        :raise KeyError: Unknown UID
        :raise OSError: Error killing the isolate
        """
        # Keep the reference to the process (just in case)
        process = self._isolates[uid]

        if process.poll() is None:
            # Fire the stop message
            try:
                self._sender.fire(
                    uid, beans.Message(cohorte.monitor.SIGNAL_STOP_ISOLATE))
            except HeraldException:
                # Error sending message
                _logger.warn(
                    "Isolate %s (PID: %d) didn't received the 'stop' "
                    "signal: Kill it!", uid, process.pid)
                try:
                    process.kill()
                except OSError as ex:
                    # Error stopping the process
                    _logger.warning("Can't kill the process: %s", ex)
            else:
                # Message sent
                try:
                    # Wait a little
                    _logger.info("Waiting for isolate %s (PID: %d) to stop...",
                                 uid, process.pid)
                    self._utils.wait_pid(process.pid, self._timeout)
                    _logger.info("Isolate stopped: %s (%d)", uid, process.pid)
                except cohorte.utils.TimeoutExpired:
                    # The isolate didn't stop -> kill the process
                    _logger.warn(
                        "Isolate timed out: %s (%d). "
                        "Trying to kill it", uid, process.pid)
                    try:
                        process.kill()
                    except OSError as ex:
                        # Error stopping the process
                        _logger.warning("Can't kill the process: %s", ex)

        # Process stopped/killed without error
        try:
            del self._isolates[uid]
        except KeyError:
            # Isolate might already be removed from this component by the
            # forker
            pass
Beispiel #18
0
 def fire(self, io_handler, target, subject, *words):
     """
     Fires a message to the given peer.
     """
     try:
         uid = self._herald.fire(target,
                                 beans.Message(subject, ' '.join(words)))
     except KeyError:
         io_handler.write_line("Unknown target: {0}", target)
     except NoTransport:
         io_handler.write_line("No transport to join {0}", target)
     else:
         io_handler.write_line("Message sent: {0}", uid)
Beispiel #19
0
 def _get_isolate_detail(self, uuid):
     lp = self._directory.get_local_peer()
     if lp.uid != uuid:
         # this is another isolate
         try:
             msg = beans.Message(debug.agent.SUBJECT_GET_ISOLATE_DETAIL)
             reply = self._herald.send(uuid, msg)
             return json.loads(reply.content)
         except KeyError:
             return None
     else:
         # this is the local isolate
         return json.loads(self._agent.get_isolate_detail())
Beispiel #20
0
 def _set_isolate_logs_level(self, uuid, level):
     lp = self._directory.get_local_peer()
     if lp.uid != uuid:
         # this is another isolate
         msg = beans.Message(debug.agent.SUBJECT_SET_ISOLATE_LOGS_LEVEL,
                             level)
         reply = self._herald.send(uuid, msg)
         return json.loads(reply.content)
     else:
         # this is the local isolate
         return json.loads(self._agent.set_isolate_logs_level(level))
     # Not yet implemented in Python
     return None
Beispiel #21
0
    def invalidate(self, context):
        """
        Component invalidated
        """
        try:
            # Send the stopping signal
            message = beans.Message(cohorte.monitor.SIGNAL_ISOLATE_STOPPING)
            self._sender.fire_group('monitors', message)
        except:
            _logger.info("Herald transports are not bound.")

        # Clear the context
        self._context = None
        self.__node_uid = None
        _logger.info("Isolate agent invalidated")
Beispiel #22
0
 def fire_group(self, io_handler, group, subject, *words):
     """
     Fires a message to the given group of peers.
     """
     try:
         uid, missed = self._herald.fire_group(
             group, beans.Message(subject, ' '.join(words)))
     except KeyError:
         io_handler.write_line("Unknown group: {0}", group)
     except NoTransport:
         io_handler.write_line("No transport to join {0}", group)
     else:
         io_handler.write_line("Message sent: {0}", uid)
         if missed:
             io_handler.write_line("Missed peers: {0}", ",".join(missed))
Beispiel #23
0
    def shutdown(self, io_handler):
        """
        Shutdown all the platform (all the nodes)
        """
        msg = beans.Message(cohorte.monitor.SIGNAL_STOP_PLATFORM)
        try:
            # send to other monitors
            self._herald.fire_group('monitors', msg)
        except herald.exceptions.HeraldException:
            pass

        # send to local peer
        msg2 = beans.MessageReceived(msg.uid, msg.subject, msg.content,
                                     "local", "local", "local")
        threading.Thread(target=self._herald.handle_message,
                         args=[msg2]).start()
 def __discover_neighbors(self):
     """
     Discover local neighbor Peers.
     The list of the neighbor peers is retrieved from the forker.
     We should ensure that the forker if properly added to the local directory
     before sending him this contact message.
     """
     try:
         reply = self._herald.send(
             self._forker.uid,
             beans.Message(SUBJECT_GET_NEIGHBORS_LIST, self._local_peer.uid))
         if reply is not None:
             for peer_uid in reply.content:
                 self.__discover_neighbor(reply.content[peer_uid])
     except Exception as ex:
         _logger.exception("Error contacting forker peer to retrieve local neighbor peers: %s", ex)
Beispiel #25
0
    def peer_registered(self, peer):
        """
        A new Herald directory group has been set.

        Sends the "Ready" message to monitors, as soon as one of their peers
        has been detected
        """
        if peer.node_uid == self.__node_uid \
                and peer.name == cohorte.FORKER_NAME:
            # Send the "ready" signal
            try:
                self._sender.fire(
                    peer, beans.Message(cohorte.monitor.SIGNAL_ISOLATE_READY))
            except herald.exceptions.NoTransport as ex:
                _logger.error("No transport to notify monitors: %s", ex)
            else:
                _logger.info("Monitors notified of isolate readiness")
Beispiel #26
0
 def get_isolate_http_port(self, uid):
     """
     Retrieves the http port of the given isolate
     """
     lp = self._directory.get_local_peer()
     if lp.uid != uid:
         msg = beans.Message(SUBJECT_GET_HTTP)
         reply = self._herald.send(uid, msg)
         return reply.content['http.port']
     else:
         # Get the isolate HTTP port
         port = -1
         svc_ref = self._context.get_service_reference(
             pelix.http.HTTP_SERVICE)
         if svc_ref is not None:
             port = svc_ref.get_property(pelix.http.HTTP_SERVICE_PORT)
         return port
Beispiel #27
0
    def herald_join(self, nick, monitor_jid, key, groups=None):
        """
        Requests to join Herald's XMPP groups

        :param nick: Multi-User Chat nick
        :param monitor_jid: JID of a monitor bot
        :param key: Key to send to the monitor bot
        :param groups: Groups to join
        """
        # Update nick for the Invite MixIn
        self._nick = nick

        # Compute & send message
        groups_str = ",".join(str(group) for group in groups
                              if group) if groups else ""
        msg = beans.Message("boostrap.invite", ":".join(
            ("invite", str(key or ''), groups_str)))
        self.__send_message("chat", monitor_jid, msg)
Beispiel #28
0
 def send(self, io_handler, target, subject, *words):
     """
     Sends a message to the given peer(s). Prints responses in the shell.
     """
     try:
         # Send the message with a 10 seconds timeout
         # (we're blocking the shell here)
         result = self._herald.send(target,
                                    beans.Message(subject, ' '.join(words)),
                                    10)
     except KeyError:
         io_handler.write_line("Unknown target: {0}", target)
     except NoTransport:
         io_handler.write_line("No transport to join {0}", target)
     except NoListener:
         io_handler.write_line("No listener for {0}", subject)
     except HeraldTimeout:
         io_handler.write_line("No response given before timeout")
     else:
         io_handler.write_line("Response: {0}", result.subject)
         io_handler.write_line(result.content)
Beispiel #29
0
    def __room_in(self, data):
        """
        Someone entered the main room

        :param data: MUC presence stanza
        """
        uid = data['from'].resource
        room_jid = data['from'].bare
        local_peer = self._directory.get_local_peer()

        if uid == local_peer.uid and room_jid == self._room:
            # We're on line, in the main room, register our service
            self._controller = True

            # Register our local access
            local_peer.set_access(self._access_id,
                                  XMPPAccess(self._bot.boundjid.full))

            # Send the "new comer" message
            message = beans.Message('herald/directory/newcomer',
                                    local_peer.dump())
            self.__send_message("groupchat", room_jid, message)
Beispiel #30
0
    def invalidate(self, _):
        """
        Component invalidated
        """
        with self.__lock:
            for session in self._sessions.values():
                peer_uid = session.get(SESSION_CLIENT_ID)
                session_id = session.get(SESSION_SESSION_ID)

                try:
                    self._herald.fire(
                        peer_uid, beans.Message(MSG_CLIENT_CLOSE, session_id))
                except HeraldException as ex:
                    # Error sending message
                    _logger.error("Error sending session close message: %s",
                                  ex)
                except KeyError:
                    # Unknown peer: ignore
                    pass

            # Clear sessions
            self._sessions.clear()