Ejemplo n.º 1
0
    def _load_ticket(self, stream_slug, destination_uuid):
        stream = Stream.get_by(slug=stream_slug)
        if not destination_uuid:
            return Ticket.get_by(stream=stream, destination=Node.me())

        node = Node.get_by(uuid=destination_uuid)
        return Ticket.query.filter_by(stream=stream, destination=node).first()
Ejemplo n.º 2
0
    def register_with_supernode(self):
        Node.update_supernode_rtt()
        # TODO hacky hacky hacky. moving query inside of the node causes
        # SQLAlchemy session errors.
        session.commit()
        session.close_all()

        if not self.node().supernode:
            if not Node.supernodes():
                self.node().supernode = True
                log.info("Registering %s as a supernode, none others found",
                        self.node())
                self.register_with_origin()
            else:
                for supernode in Node.supernodes().order_by('rtt'):
                    self.node().primary_supernode = supernode
                    try:
                        NodesAPI(self.node().primary_supernode.uri()).register(
                                self.node().to_dict())
                    except RequestError, e:
                        log.warning("Can't connect to supernode %s to register"
                                ": %s", self.node().primary_supernode, e)
                        log.info("Informing web server that supernode %s is "
                                "unresponsive and should be deleted", supernode)
                        NodesAPI(settings.ASTRAL_WEBSERVER).unregister(
                                supernode.absolute_url())
                        self.node().primary_supernode = None
                    except RequestFailed, e:
                        log.warning("%s rejected us as a child node: %s",
                                supernode, e)
                        self.node().primary_supernode = None
                    else:
                        self.load_dynamic_bootstrap_nodes(
                                self.node().primary_supernode.uri())
Ejemplo n.º 3
0
 def run(self):
     if self.ticket.confirmed and not self.ticket.source == Node.me():
         if self.ticket.destination == Node.me():
             if self.request.remote_ip == '127.0.0.1':
                 log.info("User is canceling %s -- must inform sender",
                         self.ticket)
                 TicketsAPI(self.ticket.source.uri()).cancel(
                         self.ticket.absolute_url())
             else:
                 log.info("%s is being deleted, we need to find another for "
                         "ourselves", self.ticket)
                 try:
                     TicketsHandler.handle_ticket_request(self.ticket.stream,
                             self.ticket.destination)
                 except HTTPError, e:
                     log.warning("We lost %s and couldn't find a "
                             "replacement to failover -- our stream is "
                             "dead: %s", self.ticket, e)
         elif self.request.remote_ip == self.ticket.source.ip_address:
             log.info("%s is being deleted by the source, must inform the "
                     "target %s", self.ticket, self.ticket.destination)
             TicketsAPI(self.ticket.destination.uri()).cancel(
                     self.ticket.absolute_url())
         elif self.request.remote_ip == self.ticket.destination.ip_address:
             log.info("%s is being deleted by the destination, must inform "
                     "the source %s", self.ticket, self.ticket.source)
             TicketsAPI(self.ticket.source.uri()).cancel(
                     self.ticket.absolute_url())
Ejemplo n.º 4
0
    def _load_ticket(self, stream_slug, destination_uuid):
        stream = Stream.get_by(slug=stream_slug)
        if not destination_uuid:
            return Ticket.get_by(stream=stream, destination=Node.me())

        node = Node.get_by(uuid=destination_uuid)
        return Ticket.query.filter_by(stream=stream, destination=node).first()
Ejemplo n.º 5
0
 def run(self):
     if self.ticket.confirmed and not self.ticket.source == Node.me():
         if self.ticket.destination == Node.me():
             if self.request.remote_ip == '127.0.0.1':
                 log.info("User is canceling %s -- must inform sender",
                          self.ticket)
                 TicketsAPI(self.ticket.source.uri()).cancel(
                     self.ticket.absolute_url())
             else:
                 log.info(
                     "%s is being deleted, we need to find another for "
                     "ourselves", self.ticket)
                 try:
                     TicketsHandler.handle_ticket_request(
                         self.ticket.stream, self.ticket.destination)
                 except HTTPError, e:
                     log.warning(
                         "We lost %s and couldn't find a "
                         "replacement to failover -- our stream is "
                         "dead: %s", self.ticket, e)
         elif self.request.remote_ip == self.ticket.source.ip_address:
             log.info(
                 "%s is being deleted by the source, must inform the "
                 "target %s", self.ticket, self.ticket.destination)
             TicketsAPI(self.ticket.destination.uri()).cancel(
                 self.ticket.absolute_url())
         elif self.request.remote_ip == self.ticket.destination.ip_address:
             log.info(
                 "%s is being deleted by the destination, must inform "
                 "the source %s", self.ticket, self.ticket.source)
             TicketsAPI(self.ticket.source.uri()).cancel(
                 self.ticket.absolute_url())
Ejemplo n.º 6
0
 def run(self):
     UpstreamCheckThread(self.node, self.upstream_limit).run()
     Node.me(refresh=True)
     self.load_static_bootstrap_nodes()
     self.load_dynamic_bootstrap_nodes()
     self.register_with_supernode()
     self.load_streams()
     session.commit()
Ejemplo n.º 7
0
 def test_already_streaming(self):
     stream = StreamFactory(source=Node.me())
     TicketFactory(stream=stream, source=Node.me(), destination=Node.me())
     tickets_before = Ticket.query.count()
     self.http_client.fetch(HTTPRequest(self.get_url(stream.tickets_url()), "POST", body=""), self.stop)
     response = self.wait()
     eq_(response.code, 200)
     eq_(Ticket.query.count(), tickets_before)
Ejemplo n.º 8
0
 def run(self):
     UpstreamCheckThread(self.node, self.upstream_limit).run()
     Node.me(refresh=True)
     self.load_static_bootstrap_nodes()
     self.load_dynamic_bootstrap_nodes()
     self.register_with_supernode()
     self.load_streams()
     session.commit()
Ejemplo n.º 9
0
 def _request_stream_from_supernodes(cls, stream, destination,
         unconfirmed_tickets=None):
     tickets = []
     for supernode in Node.supernodes():
         if supernode != Node.me() and not cls._already_ticketed(
                 unconfirmed_tickets, destination):
             tickets.append(cls._request_stream_from_node(stream,
                 supernode, destination))
     return filter(None, tickets)
Ejemplo n.º 10
0
 def test_get_streams(self):
     Node.me()
     [StreamFactory() for _ in range(3)]
     response = self.fetch('/streams')
     eq_(response.code, 200)
     result = json.loads(response.body)
     ok_('streams' in result)
     for stream in result['streams']:
         ok_(Stream.get_by(name=stream['name']))
Ejemplo n.º 11
0
 def test_get_tickets(self):
     response = self.fetch("/tickets")
     eq_(response.code, 200)
     result = json.loads(response.body)
     ok_("tickets" in result)
     eq_(len(result["tickets"]), 3)
     for ticket in result["tickets"]:
         source = Node.get_by(uuid=ticket["source"])
         destination = Node.get_by(uuid=ticket["destination"])
         ok_(Ticket.get_by(stream=Stream.get_by(slug=ticket["stream"]), source=source, destination=destination))
Ejemplo n.º 12
0
 def test_trigger_locally(self):
     stream = StreamFactory(source=Node.me())
     self.http_client.fetch(HTTPRequest(self.get_url(stream.tickets_url()), "POST", body=""), self.stop)
     response = self.wait()
     eq_(response.code, 200)
     result = json.loads(response.body)
     ok_("ticket" in result)
     eq_(result["ticket"]["stream"], stream.slug)
     eq_(result["ticket"]["source"], Node.me().uuid)
     ticket = Ticket.query.first()
     eq_(ticket.stream, stream)
     eq_(ticket.destination, Node.me())
Ejemplo n.º 13
0
 def _request_stream_from_supernodes(cls,
                                     stream,
                                     destination,
                                     unconfirmed_tickets=None):
     tickets = []
     for supernode in Node.supernodes():
         if supernode != Node.me() and not cls._already_ticketed(
                 unconfirmed_tickets, destination):
             tickets.append(
                 cls._request_stream_from_node(stream, supernode,
                                               destination))
     return filter(None, tickets)
Ejemplo n.º 14
0
    def _cancel_tickets(self):
        for ticket in Ticket.query.filter_by(source=Node.me()).filter(
                Ticket.destination != Node.me()):
            log.info("Cancelling %s", ticket)
            if ticket.destination:
                TicketsAPI(ticket.destination.uri()).cancel(ticket.absolute_url())

        for ticket in Ticket.query.filter_by(destination=Node.me()).filter(
                Ticket.source != Node.me()):
            log.info("Cancelling %s", ticket)
            if ticket.source:
                TicketsAPI(ticket.source.uri()).cancel(ticket.absolute_url())
        Ticket.query.delete()
Ejemplo n.º 15
0
    def _cancel_tickets(self):
        for ticket in Ticket.query.filter_by(source=Node.me()).filter(
                Ticket.destination != Node.me()):
            log.info("Cancelling %s", ticket)
            if ticket.destination:
                TicketsAPI(ticket.destination.uri()).cancel(
                    ticket.absolute_url())

        for ticket in Ticket.query.filter_by(destination=Node.me()).filter(
                Ticket.source != Node.me()):
            log.info("Cancelling %s", ticket)
            if ticket.source:
                TicketsAPI(ticket.source.uri()).cancel(ticket.absolute_url())
        Ticket.query.delete()
Ejemplo n.º 16
0
 def test_other_known_tickets(self):
     node = Node.me()
     node.supernode = True
     stream = StreamFactory()
     existing_ticket = TicketFactory(stream=stream, source=stream.source, hops=1)
     mockito.when(TicketsAPI).create(mockito.any(), destination_uuid=mockito.any()).thenReturn(
         {"source": existing_ticket.destination.uuid, "source_port": 42, "hops": 1}
     )
     tickets_before = Ticket.query.count()
     self.http_client.fetch(HTTPRequest(self.get_url(stream.tickets_url()), "POST", body=""), self.stop)
     response = self.wait()
     eq_(response.code, 200)
     eq_(Ticket.query.count(), tickets_before + 1)
     ticket = Ticket.query.filter_by(destination=Node.me()).first()
     eq_(ticket.source, existing_ticket.destination)
Ejemplo n.º 17
0
 def test_delete_node(self):
     node = NodeFactory()
     self.http_client.fetch(HTTPRequest(
         self.get_url(node.absolute_url()), 'DELETE'), self.stop)
     response = self.wait()
     eq_(response.code, 200)
     eq_(Node.get_by(uuid=node.uuid), None)
Ejemplo n.º 18
0
    def _request_stream_from_others(cls, stream, destination):
            unconfirmed_tickets = cls._request_stream(stream, destination)
            if not unconfirmed_tickets:
                raise HTTPError(412)
            unconfirmed_tickets = set(unconfirmed_tickets)
            bad_tickets = set()
            for ticket in unconfirmed_tickets:
                ticket.source.update_rtt()
                if Node.me().supernode and not ticket.source.rtt:
                    log.info("Informing web server that %s is unresponsive "
                            "and should be deleted", ticket.source)
                    NodesAPI(settings.ASTRAL_WEBSERVER).unregister(
                            ticket.source.absolute_url())
                    bad_tickets.append(ticket)
            unconfirmed_tickets = unconfirmed_tickets - bad_tickets
            log.debug("Received %d unconfirmed tickets: %s",
                    len(unconfirmed_tickets), unconfirmed_tickets)

            closest = min(unconfirmed_tickets, key=lambda t: t.source.rtt)
            log.debug("Closest ticket of the unconfirmed ones is %s", closest)
            TicketsAPI(closest.source.uri()).confirm(closest.absolute_url())
            closest.confirmed = True
            session.commit()
            for ticket in set(unconfirmed_tickets) - set([closest]):
                TicketsAPI(ticket.source.uri()).cancel(ticket.absolute_url())
                ticket.delete()
            session.commit()
            return closest
Ejemplo n.º 19
0
    def _request_stream_from_others(cls, stream, destination):
        unconfirmed_tickets = cls._request_stream(stream, destination)
        if not unconfirmed_tickets:
            raise HTTPError(412)
        unconfirmed_tickets = set(unconfirmed_tickets)
        bad_tickets = set()
        for ticket in unconfirmed_tickets:
            ticket.source.update_rtt()
            if Node.me().supernode and not ticket.source.rtt:
                log.info(
                    "Informing web server that %s is unresponsive "
                    "and should be deleted", ticket.source)
                NodesAPI(settings.ASTRAL_WEBSERVER).unregister(
                    ticket.source.absolute_url())
                bad_tickets.append(ticket)
        unconfirmed_tickets = unconfirmed_tickets - bad_tickets
        log.debug("Received %d unconfirmed tickets: %s",
                  len(unconfirmed_tickets), unconfirmed_tickets)

        closest = min(unconfirmed_tickets, key=lambda t: t.source.rtt)
        log.debug("Closest ticket of the unconfirmed ones is %s", closest)
        TicketsAPI(closest.source.uri()).confirm(closest.absolute_url())
        closest.confirmed = True
        session.commit()
        for ticket in set(unconfirmed_tickets) - set([closest]):
            TicketsAPI(ticket.source.uri()).cancel(ticket.absolute_url())
            ticket.delete()
        session.commit()
        return closest
Ejemplo n.º 20
0
 def queue_tunnel_status_flip(self):
     """If the stream went from disable to enable or vice versa, need to flip
     the status of the tunnel. Also, every local stream needs a local tunnel
     so we have something to control.
     """
     if self.source == Node.me():
         TUNNEL_QUEUE.put(self)
Ejemplo n.º 21
0
 def queue_tunnel_status_flip(self):
     """If the stream went from disable to enable or vice versa, need to flip
     the status of the tunnel. Also, every local stream needs a local tunnel
     so we have something to control.
     """
     if self.source == Node.me():
         TUNNEL_QUEUE.put(self)
Ejemplo n.º 22
0
 def test_create(self):
     stream = StreamFactory(source=Node.me())
     node = NodeFactory()
     self.http_client.fetch(
         HTTPRequest(self.get_url(stream.tickets_url()), "POST", body=json.dumps({"destination_uuid": node.uuid})),
         self.stop,
     )
     response = self.wait()
     eq_(response.code, 200)
     result = json.loads(response.body)
     ok_("ticket" in result)
     eq_(result["ticket"]["stream"], stream.slug)
     eq_(result["ticket"]["source"], Node.me().uuid)
     ticket = Ticket.query.first()
     eq_(ticket.stream, stream)
     eq_(ticket.destination, node)
Ejemplo n.º 23
0
    def handle_ticket_request(cls, stream, destination):
        log.debug("Trying to create a ticket to serve %s to %s", stream,
                  destination)
        new_ticket = cls._already_streaming(stream, destination)
        if new_ticket:
            log.info("%s already has a ticket for %s (%s)", destination,
                     stream, new_ticket)
            if not new_ticket.source == Node.me():
                log.info("Refreshing with source %s to be sure",
                         new_ticket.source)
                existing_ticket = cls._request_stream_from_node(
                    stream, new_ticket.source, destination)
            else:
                existing_ticket = new_ticket
            if existing_ticket:
                existing_ticket.refreshed = datetime.datetime.now()
                # In case we lost the tunnel, just make sure it exists
                existing_ticket.queue_tunnel_creation()
                session.commit()
                return existing_ticket
            log.info(
                "%s didn't confirm our old ticket %s, must get a new "
                "one", new_ticket.source, new_ticket)

        if stream.source != Node.me():
            new_ticket = cls._offer_ourselves(stream, destination)
            if new_ticket:
                log.info("We can stream %s to %s, created %s", stream,
                         destination, new_ticket)
                # In case we lost the tunnel, just make sure it exists
                new_ticket.queue_tunnel_creation()
            elif Node.me().supernode or destination == Node.me():
                log.info(
                    "Propagating the request for streaming %s to %s to "
                    "our other known nodes", stream, destination)
                new_ticket = cls._request_stream_from_others(
                    stream, destination)
            else:
                raise HTTPError(412)
        else:
            new_ticket = Ticket(stream=stream,
                                source_port=stream.source_port,
                                destination=destination)
            log.info("%s is the source of %s, created %s", Node.me(), stream,
                     new_ticket)
        session.commit()
        return new_ticket
Ejemplo n.º 24
0
    def _offer_ourselves(cls, stream, destination):
        tickets = Ticket.query.filter_by(source=Node.me())
        if (tickets.count() >= settings.OUTGOING_STREAM_LIMIT
                or Node.me().upstream and tickets.count()
                    * settings.STREAM_BITRATE > Node.me().upstream):
            log.info("Can't stream %s to %s, already at limit", stream,
                    destination)
            raise HTTPError(412)

        ticket = Ticket.get_by(stream=stream, destination=Node.me())
        if ticket:
            new_ticket = Ticket(stream=stream, destination=destination,
                    source_port=ticket.source_port, hops=ticket.hops + 1)
            log.info("We are receiving %s and have room to forward -- "
                "created %s to potentially forward to %s", stream, new_ticket,
                destination)
            return new_ticket
Ejemplo n.º 25
0
 def test_delete_node(self):
     node = NodeFactory()
     self.http_client.fetch(
         HTTPRequest(self.get_url(node.absolute_url()), 'DELETE'),
         self.stop)
     response = self.wait()
     eq_(response.code, 200)
     eq_(Node.get_by(uuid=node.uuid), None)
Ejemplo n.º 26
0
 def test_delete(self):
     node = Node.me()
     ticket = TicketFactory(destination=node)
     self.http_client.fetch(HTTPRequest(
         self.get_url(ticket.absolute_url()), 'DELETE'), self.stop)
     response = self.wait()
     eq_(response.code, 200)
     eq_(Ticket.get_by(id=ticket.id), None)
     ok_(Stream.get_by(slug=ticket.stream.slug))
Ejemplo n.º 27
0
    def handle_ticket_request(cls, stream, destination):
        log.debug("Trying to create a ticket to serve %s to %s",
                stream, destination)
        new_ticket = cls._already_streaming(stream, destination)
        if new_ticket:
            log.info("%s already has a ticket for %s (%s)", destination,
                    stream, new_ticket)
            if not new_ticket.source == Node.me():
                log.info("Refreshing with source %s to be sure",
                        new_ticket.source)
                existing_ticket = cls._request_stream_from_node(stream,
                        new_ticket.source, destination)
            else:
                existing_ticket = new_ticket
            if existing_ticket:
                existing_ticket.refreshed = datetime.datetime.now()
                # In case we lost the tunnel, just make sure it exists
                existing_ticket.queue_tunnel_creation()
                session.commit()
                return existing_ticket
            log.info("%s didn't confirm our old ticket %s, must get a new "
                    "one", new_ticket.source, new_ticket)

        if stream.source != Node.me():
            new_ticket = cls._offer_ourselves(stream, destination)
            if new_ticket:
                log.info("We can stream %s to %s, created %s",
                    stream, destination, new_ticket)
                # In case we lost the tunnel, just make sure it exists
                new_ticket.queue_tunnel_creation()
            elif Node.me().supernode or destination == Node.me():
                log.info("Propagating the request for streaming %s to %s to "
                        "our other known nodes", stream, destination)
                new_ticket = cls._request_stream_from_others(stream,
                        destination)
            else:
                raise HTTPError(412)
        else:
            new_ticket = Ticket(stream=stream, source_port=stream.source_port,
                    destination=destination)
            log.info("%s is the source of %s, created %s", Node.me(),
                    stream, new_ticket)
        session.commit()
        return new_ticket
Ejemplo n.º 28
0
 def test_delete(self):
     node = Node.me()
     ticket = TicketFactory(destination=node)
     self.http_client.fetch(
         HTTPRequest(self.get_url(ticket.absolute_url()), 'DELETE'),
         self.stop)
     response = self.wait()
     eq_(response.code, 200)
     eq_(Ticket.get_by(id=ticket.id), None)
     ok_(Stream.get_by(slug=ticket.stream.slug))
Ejemplo n.º 29
0
 def test_confirm(self):
     node = Node.me()
     ticket = TicketFactory(destination=node, confirmed=False)
     data = {'confirmed': True}
     eq_(ticket.confirmed, False)
     self.http_client.fetch(HTTPRequest(
         self.get_url(ticket.absolute_url()), 'PUT', body=json.dumps(data)),
         self.stop)
     response = self.wait()
     eq_(response.code, 200)
     eq_(ticket.confirmed, True)
Ejemplo n.º 30
0
    def _offer_ourselves(cls, stream, destination):
        tickets = Ticket.query.filter_by(source=Node.me())
        if (tickets.count() >= settings.OUTGOING_STREAM_LIMIT
                or Node.me().upstream and tickets.count() *
                settings.STREAM_BITRATE > Node.me().upstream):
            log.info("Can't stream %s to %s, already at limit", stream,
                     destination)
            raise HTTPError(412)

        ticket = Ticket.get_by(stream=stream, destination=Node.me())
        if ticket:
            new_ticket = Ticket(stream=stream,
                                destination=destination,
                                source_port=ticket.source_port,
                                hops=ticket.hops + 1)
            log.info(
                "We are receiving %s and have room to forward -- "
                "created %s to potentially forward to %s", stream, new_ticket,
                destination)
            return new_ticket
Ejemplo n.º 31
0
 def _handle_ticket(self, ticket):
     # reload to get a new database session
     ticket = Ticket.get_by(id=ticket.id)
     if ticket.source == Node.me():
         source_ip = "127.0.0.1"
     else:
         source_ip = ticket.source.ip_address
     try:
         port = self.create_tunnel(ticket.id, source_ip, ticket.source_port,
                                   self.tunnels)
     except Exception, e:
         log.warning("Couldn't create a tunnel for %s: %s", ticket, e)
Ejemplo n.º 32
0
 def test_confirm(self):
     node = Node.me()
     ticket = TicketFactory(destination=node, confirmed=False)
     data = {'confirmed': True}
     eq_(ticket.confirmed, False)
     self.http_client.fetch(
         HTTPRequest(self.get_url(ticket.absolute_url()),
                     'PUT',
                     body=json.dumps(data)), self.stop)
     response = self.wait()
     eq_(response.code, 200)
     eq_(ticket.confirmed, True)
Ejemplo n.º 33
0
 def test_get(self):
     node = Node.me()
     ticket = TicketFactory(destination=node)
     mockito.when(TicketsAPI).create(mockito.any(),
             destination_uuid=mockito.any()).thenReturn(
                     {'source': ticket.destination.uuid,
                         'source_port': ticket.source_port,
                         'hops': ticket.hops})
     response = self.fetch(ticket.absolute_url())
     eq_(response.code, 200)
     result = json.loads(response.body)
     ok_('ticket' in result)
     eq_(result['ticket']['stream'], ticket.stream.slug)
Ejemplo n.º 34
0
 def _handle_ticket(self, ticket):
     # reload to get a new database session
     ticket = Ticket.get_by(id=ticket.id)
     if ticket.source == Node.me():
         source_ip = "127.0.0.1"
     else:
         source_ip = ticket.source.ip_address
     try:
         port = self.create_tunnel(ticket.id, source_ip,
                 ticket.source_port, self.tunnels)
     except Exception, e:
         log.warning("Couldn't create a tunnel for %s: %s",
                 ticket, e)
Ejemplo n.º 35
0
 def test_remote_source(self):
     node = Node.me()
     node.supernode = True
     remote_node = NodeFactory()
     mockito.when(TicketsAPI).create(mockito.any(), destination_uuid=mockito.any()).thenReturn(
         {"source": remote_node.uuid, "source_port": 42, "hops": 1}
     )
     stream = StreamFactory()
     tickets_before = Ticket.query.count()
     self.http_client.fetch(HTTPRequest(self.get_url(stream.tickets_url()), "POST", body=""), self.stop)
     response = self.wait()
     eq_(response.code, 200)
     eq_(Ticket.query.count(), tickets_before + 1)
Ejemplo n.º 36
0
 def from_dict(cls, data):
     stream = Stream.get_by(name=data['name'])
     if not stream:
         source = Node.get_by(uuid=data.get('source') or
                 data.get('source_uuid'))
         if not source:
             log.debug("%s had a source not in our database -- not creating " 
                     "Stream", data)
             return
         else:
             stream = cls(source=source, name=data['name'],
                     description=data.get('description', ''))
             session.commit()
     return stream
Ejemplo n.º 37
0
 def _request_stream_from_node(cls, stream, node, destination,
         existing_ticket=None):
     if not node or node == Node.me() or node == destination:
         return
     log.info("Requesting %s from the node %s, to be delivered to %s",
             stream, node, destination)
     try:
         ticket_data = TicketsAPI(node.uri()).create(stream.tickets_url(),
                 destination_uuid=destination.uuid)
     except RequestError, e:
         log.info("Couldn't connect to %s to ask for %s -- deleting "
                 "the node from the database", node, stream)
         log.debug("Node returned: %s", e)
         node.delete()
Ejemplo n.º 38
0
    def register_with_supernode(self):
        Node.update_supernode_rtt()
        # TODO hacky hacky hacky. moving query inside of the node causes
        # SQLAlchemy session errors.
        session.commit()
        session.close_all()

        if not self.node().supernode:
            if not Node.supernodes():
                self.node().supernode = True
                log.info("Registering %s as a supernode, none others found",
                         self.node())
                self.register_with_origin()
            else:
                for supernode in Node.supernodes().order_by('rtt'):
                    self.node().primary_supernode = supernode
                    try:
                        NodesAPI(self.node().primary_supernode.uri()).register(
                            self.node().to_dict())
                    except RequestError, e:
                        log.warning(
                            "Can't connect to supernode %s to register"
                            ": %s",
                            self.node().primary_supernode, e)
                        log.info(
                            "Informing web server that supernode %s is "
                            "unresponsive and should be deleted", supernode)
                        NodesAPI(settings.ASTRAL_WEBSERVER).unregister(
                            supernode.absolute_url())
                        self.node().primary_supernode = None
                    except RequestFailed, e:
                        log.warning("%s rejected us as a child node: %s",
                                    supernode, e)
                        self.node().primary_supernode = None
                    else:
                        self.load_dynamic_bootstrap_nodes(
                            self.node().primary_supernode.uri())
Ejemplo n.º 39
0
 def from_dict(cls, data):
     stream = Stream.get_by(name=data['name'])
     if not stream:
         source = Node.get_by(
             uuid=data.get('source') or data.get('source_uuid'))
         if not source:
             log.debug(
                 "%s had a source not in our database -- not creating "
                 "Stream", data)
             return
         else:
             stream = cls(source=source,
                          name=data['name'],
                          description=data.get('description', ''))
             session.commit()
     return stream
Ejemplo n.º 40
0
 def test_get(self):
     node = Node.me()
     ticket = TicketFactory(destination=node)
     mockito.when(TicketsAPI).create(
         mockito.any(), destination_uuid=mockito.any()).thenReturn({
             'source':
             ticket.destination.uuid,
             'source_port':
             ticket.source_port,
             'hops':
             ticket.hops
         })
     response = self.fetch(ticket.absolute_url())
     eq_(response.code, 200)
     result = json.loads(response.body)
     ok_('ticket' in result)
     eq_(result['ticket']['stream'], ticket.stream.slug)
Ejemplo n.º 41
0
 def _request_stream_from_node(cls,
                               stream,
                               node,
                               destination,
                               existing_ticket=None):
     if not node or node == Node.me() or node == destination:
         return
     log.info("Requesting %s from the node %s, to be delivered to %s",
              stream, node, destination)
     try:
         ticket_data = TicketsAPI(node.uri()).create(
             stream.tickets_url(), destination_uuid=destination.uuid)
     except RequestError, e:
         log.info(
             "Couldn't connect to %s to ask for %s -- deleting "
             "the node from the database", node, stream)
         log.debug("Node returned: %s", e)
         node.delete()
Ejemplo n.º 42
0
 def post(self):
     """Add the node specified in POSTed JSON to the list of known nodes."""
     uuid = self.get_json_argument('uuid')
     if (Node.me().supernode and self.get_json_argument(
                 'primary_supernode_uuid', '') == Node.me().uuid):
         children_count = Node.query.filter_by(primary_supernode=Node.me()
                 ).count()
         if children_count > settings.SUPERNODE_MAX_CHILDREN:
             raise HTTPError(503)
     if not Node.get_by(uuid=uuid):
         # TODO what if supernode changes? need to update
         self.request.arguments.setdefault('ip_address',
                 self.request.remote_ip)
         Node.from_dict(self.request.arguments)
         session.commit()
Ejemplo n.º 43
0
 def get(self, stream_slug, destination_uuid=None):
     ticket = self._load_ticket(stream_slug, destination_uuid)
     if ticket:
         # TODO this block is somewhat duplicated from TicketsHandler.post,
         # where we refresh an existing ticket.
         if not ticket.source == Node.me():
             log.info("Refreshing %s with the source", ticket)
             ticket = TicketsHandler._request_stream_from_node(ticket.stream,
                     ticket.source, ticket.destination,
                     existing_ticket=ticket)
         if ticket:
             ticket.refreshed = datetime.datetime.now()
             # In case we lost the tunnel, just make sure it exists
             ticket.queue_tunnel_creation()
             session.commit()
             # TODO this is unideal, but we need to get the new port if it
             # changed. combination of sleep and db flush seems to do it
             # somewhat reliably, but it's still a race condition.
             import time
             time.sleep(0.5)
         ticket = self._load_ticket(stream_slug, destination_uuid)
         self.write({'ticket': ticket.to_dict()})
Ejemplo n.º 44
0
 def get(self, stream_slug, destination_uuid=None):
     ticket = self._load_ticket(stream_slug, destination_uuid)
     if ticket:
         # TODO this block is somewhat duplicated from TicketsHandler.post,
         # where we refresh an existing ticket.
         if not ticket.source == Node.me():
             log.info("Refreshing %s with the source", ticket)
             ticket = TicketsHandler._request_stream_from_node(
                 ticket.stream,
                 ticket.source,
                 ticket.destination,
                 existing_ticket=ticket)
         if ticket:
             ticket.refreshed = datetime.datetime.now()
             # In case we lost the tunnel, just make sure it exists
             ticket.queue_tunnel_creation()
             session.commit()
             # TODO this is unideal, but we need to get the new port if it
             # changed. combination of sleep and db flush seems to do it
             # somewhat reliably, but it's still a race condition.
             import time
             time.sleep(0.5)
         ticket = self._load_ticket(stream_slug, destination_uuid)
         self.write({'ticket': ticket.to_dict()})
Ejemplo n.º 45
0
    def post(self):
        """Register a new available stream."""
        # TODO kind of messy way to handle two different data types, but for
        # some reason Torando is loading the name and description as lists
        # instead of strings if they are form encded
        if not hasattr(self.request, 'arguments') or not self.request.arguments:
            self.load_json()
        else:
            self.request.arguments['name'] = self.request.arguments['name'][0]
            self.request.arguments['description'] = (
                    self.request.arguments.get('description', [''])[0])

        self.request.arguments.setdefault('source', Node.me().uuid)
        if Stream.get_by(name=self.request.arguments['name']):
            self.redirect("%s/upload" % settings.ASTRAL_WEBSERVER)
            return
        stream = Stream.from_dict(self.request.arguments)
        try:
            StreamsAPI(settings.ASTRAL_WEBSERVER).create(
                    source_uuid=stream.source.uuid, name=stream.name,
                    slug=stream.slug, description=stream.description)
        except RequestError, e:
            log.warning("Unable to register stream with origin webserver: %s",
                    e)
Ejemplo n.º 46
0
 def load_static_bootstrap_nodes(self):
     log.info("Loading static bootstrap nodes %s", settings.BOOTSTRAP_NODES)
     nodes = [Node.from_dict(node) for node in settings.BOOTSTRAP_NODES]
     session.commit()
     log.debug("Loaded static bootstrap nodes %s", nodes)
Ejemplo n.º 47
0
 def prime_stream_tunnels(self):
     for stream in Stream.query.filter(Stream.source == Node.me()):
         stream.queue_tunnel_status_flip()
Ejemplo n.º 48
0
 def setUp(self):
     super(TicketsListHandlerTest, self).setUp()
     Node.me()
     [TicketFactory() for _ in range(3)]
Ejemplo n.º 49
0
 def _unregister_from_all(self):
     for node in Node.not_me():
         if node != Node.me().primary_supernode:
             log.info("Unregistering from %s", node)
             NodesAPI(node.uri()).unregister(self.node().absolute_url())
Ejemplo n.º 50
0
 def setUp(self):
     super(BaseNodeTest, self).setUp()
     self.node = Node.me()
Ejemplo n.º 51
0
    def load_dynamic_bootstrap_nodes(self, base_url=None):
        base_url = base_url or settings.ASTRAL_WEBSERVER
        try:
            nodes = NodesAPI(base_url).list()
        except RequestError, e:
            log.warning("Can't connect to server: %s", e)
        else:
            log.debug("Nodes returned from the server: %s", nodes)
            for node in nodes:
                if self.node().conflicts_with(node):
                    log.warn(
                        "Received %s which conflicts with us (%s) "
                        "-- telling web server to kill it", node, self.node())
                    NodesAPI(base_url).unregister(self.node().absolute_url())
                else:
                    node = Node.from_dict(node)
                    log.info("Stored %s from %s", node, base_url)

    def load_streams(self, base_url=None):
        base_url = base_url or settings.ASTRAL_WEBSERVER
        try:
            streams = StreamsAPI(base_url).list()
        except RequestError, e:
            log.warning("Can't connect to server: %s", e)
        else:
            log.debug("Streams returned from the server: %s", streams)
            for stream in streams:
                stream = Stream.from_dict(stream)
                if stream:
                    log.info("Stored %s from %s", stream, base_url)
        self.prime_stream_tunnels()
Ejemplo n.º 52
0
                stream.tickets_url(), destination_uuid=destination.uuid)
        except RequestError, e:
            log.info(
                "Couldn't connect to %s to ask for %s -- deleting "
                "the node from the database", node, stream)
            log.debug("Node returned: %s", e)
            node.delete()
        except RequestFailed, e:
            if e.response.status_int == 412:
                log.debug("%s didn't have room to forward %s to us", node,
                          stream)
        else:
            if existing_ticket:
                return existing_ticket
            if ticket_data:
                source = Node.get_by(uuid=ticket_data['source'])
                if not source:
                    source_node_data = NodesAPI(node.uri()).get(
                        Node.absolute_url(source))
                    source = Node.from_dict(source_node_data)
                return Ticket(stream=stream,
                              source=source,
                              source_port=ticket_data['source_port'],
                              destination=destination,
                              hops=ticket_data['hops'] + 1)

    @classmethod
    def _request_stream_from_watchers(cls,
                                      stream,
                                      destination,
                                      unconfirmed_tickets=None):
Ejemplo n.º 53
0
 def _already_seeding(cls, ticket):
     return Node.me() in [ticket.destination, ticket.stream.source]
Ejemplo n.º 54
0
 def test_create_twice_locally(self):
     stream = StreamFactory(source=Node.me())
     TicketFactory(stream=stream, source=Node.me(), destination=Node.me())
     self.http_client.fetch(HTTPRequest(self.get_url(stream.tickets_url()), "POST", body=""), self.stop)
     response = self.wait()
     eq_(response.code, 200)
Ejemplo n.º 55
0
 def node(self):
     return Node.get_by(uuid=self.uuid) or Node.me(uuid_override=self.uuid)