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())
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())
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()
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)
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']))
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())
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()
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()
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)
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()
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)
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()
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
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
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)
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
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
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)
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))
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
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))
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
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)
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)
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)
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)
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)
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)
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()
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)
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()
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()})
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()})
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)
def setUp(self): super(BaseNodeTest, self).setUp() self.node = Node.me()
self.node().primary_supernode = None else: self.load_dynamic_bootstrap_nodes( self.node().primary_supernode.uri()) if not self.node().primary_supernode: self.node().supernode = True log.info( "No supernode could take us - " "registering ourselves %s as a supernode", self.node()) self.register_with_origin() else: log.info("Registering %s as a supernode, my database told me so", self.node()) self.register_with_origin() for supernode in Node.supernodes(): if supernode != Node.me(): try: NodesAPI(supernode.uri()).register( self.node().to_dict()) except RequestError, e: log.warning("Can't connect to supernode %s: %s", supernode, e) supernode.delete() log.info( "Informing web server that %s is unresponsive " "and should be deleted", supernode) NodesAPI(settings.ASTRAL_WEBSERVER).unregister( supernode.absolute_url()) except RequestFailed: log.warning( "%s threw an error - sure it's not "
if not stream: log.debug("Couldnt find stream with slug %s anywhere", stream_slug) raise HTTPError(404) destination_uuid = self.get_json_argument('destination_uuid', '') if destination_uuid: destination = Node.get_by(uuid=destination_uuid) # TODO since we only have the IP, we have to assume the port is 8000 # to be able to request back to it for more details. hmm. # TODO another problem is that the tornado server is (and i should # have realized this sooner...) single-threaded, and based on the # event model. So the requsting node is blocked waiting for us to # responsed, then we go and query it. well, that's deadlock! a # workaroud since we're only dealing with single supernode # situations is just to query the supernode, since they MUST know # about that other node. if not destination and Node.me().primary_supernode: try: log.debug("Don't know of a node with UUID %s for the " "ticket destination -- asking the requester at " "http://%s:%s", destination_uuid, self.request.remote_ip, settings.PORT) # TODO here in the future we would change it to the # remote_ip. now just does supernode. node_data = NodesAPI(Node.me().primary_supernode.uri() ).find(destination_uuid) except RequestError, e: log.warning("Can't connect to server: %s", e) except ResourceNotFound: log.debug("Request didn't know of a node with UUID", destination_uuid) raise HTTPError(404)
def _already_seeding(cls, ticket): return Node.me() in [ticket.destination, ticket.stream.source]
def prime_stream_tunnels(self): for stream in Stream.query.filter(Stream.source == Node.me()): stream.queue_tunnel_status_flip()
self.node().primary_supernode = None else: self.load_dynamic_bootstrap_nodes( self.node().primary_supernode.uri()) if not self.node().primary_supernode: self.node().supernode = True log.info("No supernode could take us - " "registering ourselves %s as a supernode", self.node()) self.register_with_origin() else: log.info("Registering %s as a supernode, my database told me so", self.node()) self.register_with_origin() for supernode in Node.supernodes(): if supernode != Node.me(): try: NodesAPI(supernode.uri()).register( self.node().to_dict()) except RequestError, e: log.warning("Can't connect to supernode %s: %s", supernode, e) supernode.delete() log.info("Informing web server that %s is unresponsive " "and should be deleted", supernode) NodesAPI(settings.ASTRAL_WEBSERVER).unregister( supernode.absolute_url()) except RequestFailed: log.warning("%s threw an error - sure it's not " "running on another computer in your LAN with " "the same remote IP?", supernode)
def setUp(self): super(TicketsListHandlerTest, self).setUp() Node.me() [TicketFactory() for _ in range(3)]
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())
if not stream: log.debug("Couldnt find stream with slug %s anywhere", stream_slug) raise HTTPError(404) destination_uuid = self.get_json_argument('destination_uuid', '') if destination_uuid: destination = Node.get_by(uuid=destination_uuid) # TODO since we only have the IP, we have to assume the port is 8000 # to be able to request back to it for more details. hmm. # TODO another problem is that the tornado server is (and i should # have realized this sooner...) single-threaded, and based on the # event model. So the requsting node is blocked waiting for us to # responsed, then we go and query it. well, that's deadlock! a # workaroud since we're only dealing with single supernode # situations is just to query the supernode, since they MUST know # about that other node. if not destination and Node.me().primary_supernode: try: log.debug( "Don't know of a node with UUID %s for the " "ticket destination -- asking the requester at " "http://%s:%s", destination_uuid, self.request.remote_ip, settings.PORT) # TODO here in the future we would change it to the # remote_ip. now just does supernode. node_data = NodesAPI( Node.me().primary_supernode.uri()).find( destination_uuid) except RequestError, e: log.warning("Can't connect to server: %s", e) except ResourceNotFound: log.debug("Request didn't know of a node with UUID",
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)
def node(self): return Node.get_by(uuid=self.uuid) or Node.me(uuid_override=self.uuid)