Example #1
0
class GetChunkLoaction(Resource):
    allowedMethods = ('GET', )

    def __init__(self, dhtstorage):
        Resource.__init__(self)
        self.dhtstorage = dhtstorage
        self.log = Logger(system=self)

    def getChild(self, path, request):
        return self

    def render_GET(self, request):
        time_keeper = TimeKeeper()
        time_id = time_keeper.start_clock_unique()

        def respond(result):
            time_keeper.stop_clock_unique(ENTRY_TOTAL_QUERY_CHUNK, time_id)
            self.log.debug(
                "%s %s %s" %
                (BENCH_TAG, TYPE_QUERY_CHUNK_ADDR, time_keeper.get_summary()))

            if result is None:
                request.setResponseCode(400)
                request.write("No Result found")
            else:
                request.setResponseCode(200)
                request.write(result)
            request.finish()

        if len(request.prepath) < 2:
            request.setResponseCode(400)
            return json.dumps({'error': "Illegal URL"})
        try:
            chunk_key = binascii.unhexlify(request.prepath[1])
            self.dhtstorage.get_addr_chunk(
                chunk_key, time_keeper=time_keeper).addCallback(respond)
            return NOT_DONE_YET
        except TalosVCRestClientError:
            request.setResponseCode(400)
            return "ERROR: No Policy found"
        except:
            request.setResponseCode(400)
            return "ERROR"
Example #2
0
class AddChunk(Resource):
    allowedMethods = ('POST', )

    def __init__(self, dhtstorage):
        Resource.__init__(self)
        self.dhtstorage = dhtstorage
        self.log = Logger(system=self)

    def render_POST(self, request):
        time_keeper = TimeKeeper()
        time_id = time_keeper.start_clock_unique()

        def respond(result):
            time_keeper.stop_clock_unique(ENTRY_TOTAL_ADD_CHUNK, time_id)
            self.log.debug(
                "%s %s %s" %
                (BENCH_TAG, TYPE_ADD_CHUNK, time_keeper.get_summary()))
            if not result is None:
                request.setResponseCode(200)
                request.write("OK")
            else:
                request.setResponseCode(400)
                request.write("ERROR")
            request.finish()

        encoded_chunk = request.content.read()
        try:

            chunk = CloudChunk.decode(encoded_chunk)
            self.dhtstorage.store_chunk(
                chunk, time_keeper=time_keeper).addCallback(respond)
            return NOT_DONE_YET
        except InvalidChunkError:
            request.setResponseCode(400)
            return "ERROR: Invalid Chunk"
        except TalosVCRestClientError:
            request.setResponseCode(400)
            return "ERROR: No Policy found"
        except:
            request.setResponseCode(400)
            return "ERROR"
Example #3
0
class KademliaProtocol(RPCProtocol):
    def __init__(self, sourceNode, storage, ksize):
        RPCProtocol.__init__(self)
        self.router = RoutingTable(self, ksize, sourceNode)
        self.storage = storage
        self.sourceNode = sourceNode
        self.log = Logger(system=self)

    def getRefreshIDs(self):
        """
        Get ids to search for to keep old buckets up to date.
        """
        ids = []
        for bucket in self.router.getLonelyBuckets():
            ids.append(random.randint(*bucket.range))
        return ids

    def rpc_stun(self, sender):
        return sender

    def rpc_ping(self, sender, nodeid):
        source = Node(nodeid, sender[0], sender[1])
        self.router.addContact(source)
        return self.sourceNode.id

    def rpc_store(self, sender, nodeid, key, value):
        source = Node(nodeid, sender[0], sender[1])
        self.router.addContact(source)
        self.log.debug("got a store request from %s, storing value" % str(sender))
        self.storage[key] = value
        return True

    def rpc_find_node(self, sender, nodeid, key):
        self.log.info("finding neighbors of %i in local table" % long(nodeid.encode('hex'), 16))
        source = Node(nodeid, sender[0], sender[1])
        self.router.addContact(source)
        node = Node(key)
        return map(tuple, self.router.findNeighbors(node, exclude=source))

    def rpc_find_value(self, sender, nodeid, key):
        source = Node(nodeid, sender[0], sender[1])
        self.router.addContact(source)
        value = self.storage.get(key, None)
        if value is None:
            return self.rpc_find_node(sender, nodeid, key)
        return { 'value': value }

    def callFindNode(self, nodeToAsk, nodeToFind):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.find_node(address, self.sourceNode.id, nodeToFind.id)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callFindValue(self, nodeToAsk, nodeToFind):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.find_value(address, self.sourceNode.id, nodeToFind.id)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callPing(self, nodeToAsk):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.ping(address, self.sourceNode.id)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callStore(self, nodeToAsk, key, value):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.store(address, self.sourceNode.id, key, value)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def transferKeyValues(self, node):
        """
        Given a new node, send it all the keys/values it should be storing.

        @param node: A new node that just joined (or that we just found out
        about).

        Process:
        For each key in storage, get k closest nodes.  If newnode is closer
        than the furtherst in that list, and the node for this server
        is closer than the closest in that list, then store the key/value
        on the new node (per section 2.5 of the paper)
        """
        ds = []
        for key, value in self.storage.iteritems():
            keynode = Node(digest(key))
            neighbors = self.router.findNeighbors(keynode)
            if len(neighbors) > 0:
                newNodeClose = node.distanceTo(keynode) < neighbors[-1].distanceTo(keynode)
                thisNodeClosest = self.sourceNode.distanceTo(keynode) < neighbors[0].distanceTo(keynode)
            if len(neighbors) == 0 or (newNodeClose and thisNodeClosest):
                ds.append(self.callStore(node, key, value))
        return defer.gatherResults(ds)

    def handleCallResponse(self, result, node):
        """
        If we get a response, add the node to the routing table.  If
        we get no response, make sure it's removed from the routing table.
        """
        if result[0]:
            self.log.info("got response from %s, adding to router" % node)
            self.router.addContact(node)
            if self.router.isNewNode(node):
                self.transferKeyValues(node)
        else:
            self.log.debug("no response from %s, removing from router" % node)
            self.router.removeContact(node)
        return result
Example #4
0
class TalosDHTServer(object):
    """
    Modified implementation of bmullers DHT for talos
    High level view of a node instance.  This is the object that should be created
    to start listening as an active node on the network.
    
    We assume public ip addresses! No NAT etc
    """

    def __init__(self, ksize=20, alpha=3, id=None, storage=None,
                 talos_vc=None, rebub_delay=3600, tls_port=-1):
        """
        Create a server instance.  This will start listening on the given port.
        Args:
            ksize (int): The k parameter from the paper
            alpha (int): The alpha parameter from the paper
            id: The id for this node on the network.
            storage: An instance that implements :interface:`~kademlia.storage.IStorage`
        """
        self.ksize = ksize
        self.alpha = alpha
        self.log = Logger(system=self)
        self.storage = storage or TalosLevelDBDHTStorage("./leveldb")
        self.node = Node(id or digest(random.getrandbits(255)))

        def start_looping_call(num_seconds):
            self.refreshLoop = LoopingCall(self.refreshTable).start(num_seconds)

        self.delay = rebub_delay
        task.deferLater(reactor, rebub_delay, start_looping_call, rebub_delay)
        self.talos_vc = talos_vc or AsyncPolicyApiClient()
        self.protocol = TalosKademliaProtocol(self.node, self.storage, ksize, talos_vc=self.talos_vc)
        self.httpprotocol_client = None
        self.tls_port = tls_port

    def listen(self, port, interface="127.0.0.1"):
        """
        Init tcp/udp protocol on the given port
        Start listening on the given port.
        """
        if self.tls_port != -1:
            root1 = Resource()
            root2 = Resource()
            root1.putChild("get_chunk", QueryChunk(self.storage, talos_vc=self.talos_vc))
            root2.putChild("storelargechunk", StoreLargeChunk(self.storage, self.protocol, talos_vc=self.talos_vc))
            factory1 = Site(root1)
            factory2 = Site(root2)

            certData = getModule(__name__).filePath.sibling('server.pem').getContent()
            certificate = ssl.PrivateCertificate.loadPEM(certData)

            self.httpprotocol_client = TalosHTTPClient(self.protocol, port)
            self.protocol.http_client = self.httpprotocol_client
            reactor.listenTCP(port, factory1, interface=interface)
            reactor.listenSSL(self.tls_port, factory2, certificate.options(), interface=interface)
            return reactor.listenUDP(port, self.protocol, interface, maxPacketSize=65535)
        else:
            root = Resource()
            root.putChild("get_chunk", QueryChunk(self.storage, talos_vc=self.talos_vc))
            root.putChild("storelargechunk", StoreLargeChunk(self.storage, self.protocol, talos_vc=self.talos_vc))
            factory = Site(root)

            self.httpprotocol_client = TalosHTTPClient(self.protocol, port)
            self.protocol.http_client = self.httpprotocol_client
            reactor.listenTCP(port, factory, interface=interface)
            return reactor.listenUDP(port, self.protocol, interface, maxPacketSize=65535)

    def refreshTable(self):
        """
        Refresh buckets that haven't had any lookups in the last hour
        (per section 2.3 of the paper).
        """
        self.log.info("Refreshing table")
        ds = []
        for id in self.protocol.getRefreshIDs():
            node = Node(id)
            nearest = self.protocol.router.findNeighbors(node, self.alpha)
            spider = NodeSpiderCrawl(self.protocol, node, nearest, self.ksize, self.alpha)
            ds.append(spider.find())

        def republishKeys(_):
            ds = []
            # Republish keys older than one hour
            for dkey, value in self.storage.iteritemsOlderThan(self.delay):
                ds.append(self.digest_set(digest(dkey), value))
            return defer.gatherResults(ds)

        return defer.gatherResults(ds).addCallback(republishKeys)

    def bootstrappableNeighbors(self):
        """
        Get a :class:`list` of (ip, port) :class:`tuple` pairs suitable for use as an argument
        to the bootstrap method.
        The server should have been bootstrapped
        already - this is just a utility for getting some neighbors and then
        storing them if this server is going down for a while.  When it comes
        back up, the list of nodes can be used to bootstrap.
        """
        neighbors = self.protocol.router.findNeighbors(self.node)
        return [tuple(n)[-2:] for n in neighbors]

    def bootstrap(self, addrs):
        """
        Bootstrap the server by connecting to other known nodes in the network.
        Args:
            addrs: A `list` of (ip, port) `tuple` pairs.  Note that only IP addresses
                   are acceptable - hostnames will cause an error.
        """
        # if the transport hasn't been initialized yet, wait a second
        if self.protocol.transport is None:
            return task.deferLater(reactor, 1, self.bootstrap, addrs)

        def initTable(results):
            nodes = []
            for addr, result in results.items():
                if result[0]:
                    nodes.append(Node(result[1], addr[0], addr[1]))
            spider = NodeSpiderCrawl(self.protocol, self.node, nodes, self.ksize, self.alpha)
            return spider.find()

        ds = {}
        for addr in addrs:
            ds[addr] = self.protocol.ping(addr, self.node.id)
        return deferredDict(ds).addCallback(initTable)

    def inetVisibleIP(self):
        """
        Get the internet visible IP's of this node as other nodes see it.
        Returns:
            A `list` of IP's.  If no one can be contacted, then the `list` will be empty.
        """

        def handle(results):
            ips = [result[1][0] for result in results if result[0]]
            self.log.debug("other nodes think our ip is %s" % str(ips))
            return ips

        ds = []
        for neighbor in self.bootstrappableNeighbors():
            ds.append(self.protocol.stun(neighbor))
        return defer.gatherResults(ds).addCallback(handle)

    def store_chunk(self, chunk, policy=None, time_keeper=TimeKeeper()):
        dkey = digest(chunk.key)
        self.log.debug("Storing chunk with key %s" % (binascii.hexlify(dkey),))
        result = self.digest_set(dkey, chunk.encode(), policy_in=policy, time_keeper=time_keeper)
        return result

    def get_addr_chunk(self, chunk_key, policy_in=None, time_keeper=TimeKeeper()):
        # if this node has it, return it
        if self.storage.has_value(chunk_key):
            addr = self.protocol.get_address()
            return defer.succeed("%s:%d" % (addr[0], addr[1]))
        dkey = digest(chunk_key)
        node = Node(dkey)
        nearest = self.protocol.router.findNeighbors(node)
        self.log.debug("Crawling for key %s" % (binascii.hexlify(dkey),))
        if len(nearest) == 0:
            self.log.warning("There are no known neighbors to get key %s" % binascii.hexlify(dkey))
            return defer.succeed(None)
        spider = TalosChunkSpiderCrawl(self.protocol, self.httpprotocol_client, node, chunk_key, nearest, self.ksize,
                                       self.alpha, time_keeper=time_keeper)
        return spider.find()

    def digest_set(self, dkey, value, policy_in=None, time_keeper=TimeKeeper()):
        """
        Set the given SHA1 digest key to the given value in the network.
        """
        node = Node(dkey)
        # this is useful for debugging messages
        hkey = binascii.hexlify(dkey)

        def _anyRespondSuccess(responses, time_keeper, id, name):
            """
            Given the result of a DeferredList of calls to peers, ensure that at least
            one of them was contacted and responded with a Truthy result.
            """
            time_keeper.stop_clock_unique(name, id)

            for deferSuccess, result in responses:
                peerReached, peerResponse = result
                if deferSuccess and peerReached and peerResponse:
                    return True
            return False

        def store(nodes):
            self.log.info("setting '%s' on %s" % (hkey, map(str, nodes)))
            # if this node is close too, then store here as well
            if self.node.distanceTo(node) < max([n.distanceTo(node) for n in nodes]):
                chunk = CloudChunk.decode(value)
                if not digest(chunk.key) == dkey:
                    return {'error': 'key missmatch'}

                def handle_policy(policy):
                    time_keeper.stop_clock(ENTRY_FETCH_POLICY)
                    # Hack no chunk id given -> no key checks, key is in the encoded chunk
                    id = time_keeper.start_clock_unique()
                    self.storage.store_check_chunk(chunk, None, policy, time_keeper=time_keeper)
                    time_keeper.stop_clock_unique(ENTRY_STORE_CHECK, id)

                    id = time_keeper.start_clock_unique()
                    ds = [self.protocol.callStore(n, dkey, value) for n in nodes]
                    return defer.DeferredList(ds).addCallback(_anyRespondSuccess, time_keeper, id,
                                                              ENTRY_STORE_TO_ALL_NODES)

                if not policy_in is None:
                    return handle_policy(policy_in)
                time_keeper.start_clock()
                return self.talos_vc.get_policy_with_txid(chunk.get_tag_hex()).addCallback(handle_policy)

            id = time_keeper.start_clock_unique()
            ds = [self.protocol.callStore(n, dkey, value) for n in nodes]
            return defer.DeferredList(ds).addCallback(_anyRespondSuccess, time_keeper, id, ENTRY_STORE_TO_ALL_NODES)

        nearest = self.protocol.router.findNeighbors(node)
        if len(nearest) == 0:
            self.log.warning("There are no known neighbors to set key %s" % hkey)
            return defer.succeed(False)
        spider = TimedNodeSpiderCrawl(self.protocol, node, nearest, self.ksize, self.alpha, time_keeper=time_keeper)
        return spider.find().addCallback(store)

    def saveState(self, fname):
        """
        Save the state of this node (the alpha/ksize/id/immediate neighbors)
        to a cache file with the given fname.
        """
        self.log.info("Save state to file %s" % fname)
        data = {'ksize': self.ksize,
                'alpha': self.alpha,
                'id': self.node.id,
                'neighbors': self.bootstrappableNeighbors()}
        if len(data['neighbors']) == 0:
            self.log.warning("No known neighbors, so not writing to cache.")
            return
        with open(fname, 'w') as f:
            pickle.dump(data, f)

    @classmethod
    def loadState(self, fname, storage=None, talos_vc=None):
        """
        Load the state of this node (the alpha/ksize/id/immediate neighbors)
        from a cache file with the given fname.
        """
        with open(fname, 'r') as f:
            data = pickle.load(f)
        s = TalosDHTServer(data['ksize'], data['alpha'], data['id'], storage=None, talos_vc=None)
        if len(data['neighbors']) > 0:
            s.bootstrap(data['neighbors'])
        return s

    def saveStateRegularly(self, fname, frequency=600):
        """
        Save the state of node with a given regularity to the given
        filename.
        Args:
            fname: File name to save retularly to
            frequencey: Frequency in seconds that the state should be saved.
                        By default, 10 minutes.
        """

        def run_looping_call(freq):
            loop = LoopingCall(self.saveState, fname).start(freq)
            return loop

        return task.deferLater(reactor, frequency, run_looping_call, frequency)
Example #5
0
class Server(object):
    """
    High level view of a node instance.  This is the object that should be created
    to start listening as an active node on the network.
    """

    def __init__(self, ksize=20, alpha=3, id=None):
        """
        Create a server instance.  This will start listening on the given port.

        @param port: UDP port to listen on
        @param k: The k parameter from the paper
        @param alpha: The alpha parameter from the paper
        """
        self.ksize = ksize
        self.alpha = alpha
        self.log = Logger(system=self)
        self.storage = ForgetfulStorage()
        self.node = Node(id or digest(random.getrandbits(255)))
        self.protocol = KademliaProtocol(self.node, self.storage, ksize)
        self.refreshLoop = LoopingCall(self.refreshTable).start(3600)

    def listen(self, port):
        """
        Start listening on the given port.

        This is the same as calling:
        C{reactor.listenUDP(port, server.protocol)}
        """
        return reactor.listenUDP(port, self.protocol)

    def refreshTable(self):
        """
        Refresh buckets that haven't had any lookups in the last hour
        (per section 2.3 of the paper).
        """
        ds = []
        for id in self.protocol.getRefreshIDs():
            node = Node(id)
            nearest = self.protocol.router.findNeighbors(node, self.alpha)
            spider = NodeSpiderCrawl(self.protocol, node, nearest)
            ds.append(spider.find())

        def republishKeys(_):
            ds = []
            # Republish keys older than one hour
            for key, value in self.storage.iteritemsOlderThan(3600):
                ds.append(self.set(key, value))
            return defer.gatherResults(ds)

        return defer.gatherResults(ds).addCallback(republishKeys)

    def bootstrappableNeighbors(self):
        """
        Get a C{list} of (ip, port) C{tuple}s suitable for use as an argument
        to the bootstrap method.

        The server should have been bootstrapped
        already - this is just a utility for getting some neighbors and then
        storing them if this server is going down for a while.  When it comes
        back up, the list of nodes can be used to bootstrap.
        """
        neighbors = self.protocol.router.findNeighbors(self.node)
        return [ tuple(n)[-2:] for n in neighbors ]

    def bootstrap(self, addrs):
        """
        Bootstrap the server by connecting to other known nodes in the network.

        @param addrs: A C{list} of (ip, port) C{tuple}s.  Note that only IP addresses
        are acceptable - hostnames will cause an error.
        """
        # if the transport hasn't been initialized yet, wait a second
        if self.protocol.transport is None:
            return task.deferLater(reactor, 1, self.bootstrap, addrs)

        def initTable(results):
            nodes = []
            for addr, result in results.items():
                if result[0]:
                    nodes.append(Node(result[1], addr[0], addr[1]))
            spider = NodeSpiderCrawl(self.protocol, self.node, nodes, self.ksize, self.alpha)
            return spider.find()

        ds = {}
        for addr in addrs:
            ds[addr] = self.protocol.ping(addr, self.node.id)
        return deferredDict(ds).addCallback(initTable)

    def inetVisibleIP(self):
        """
        Get the internet visible IP's of this node as other nodes see it.

        @return: An C{list} of IP's.  If no one can be contacted, then the
        C{list} will be empty.
        """
        def handle(results):
            ips = [ result[1][0] for result in results if result[0] ]
            self.log.debug("other nodes think our ip is %s" % str(ips))
            return ips

        ds = []
        for neighbor in self.bootstrappableNeighbors():
            ds.append(self.protocol.stun(neighbor))
        return defer.gatherResults(ds).addCallback(handle)

    def get(self, key):
        """
        Get a key if the network has it.

        @return: C{None} if not found, the value otherwise.
        """
        node = Node(digest(key))
        nearest = self.protocol.router.findNeighbors(node)
        if len(nearest) == 0:
            self.log.warning("There are no known neighbors to get key %s" % key)
            return defer.succeed(None)
        spider = ValueSpiderCrawl(self.protocol, node, nearest, self.ksize, self.alpha)
        return spider.find()

    def set(self, key, value):
        """
        Set the given key to the given value in the network.
        """
        self.log.debug("setting '%s' = '%s' on network" % (key, value))
        dkey = digest(key)

        def store(nodes):
            self.log.info("setting '%s' on %s" % (key, map(str, nodes)))
            ds = [self.protocol.callStore(node, dkey, value) for node in nodes]
            return defer.DeferredList(ds).addCallback(self._anyRespondSuccess)

        node = Node(dkey)
        nearest = self.protocol.router.findNeighbors(node)
        if len(nearest) == 0:
            self.log.warning("There are no known neighbors to set key %s" % key)
            return defer.succeed(False)
        spider = NodeSpiderCrawl(self.protocol, node, nearest, self.ksize, self.alpha)
        return spider.find().addCallback(store)

    def _anyRespondSuccess(self, responses):
        """
        Given the result of a DeferredList of calls to peers, ensure that at least
        one of them was contacted and responded with a Truthy result.
        """
        for deferSuccess, result in responses:
            peerReached, peerResponse = result
            if deferSuccess and peerReached and peerResponse:
                return True
        return False

    def saveState(self, fname):
        """
        Save the state of this node (the alpha/ksize/id/immediate neighbors)
        to a cache file with the given fname.
        """
        data = { 'ksize': self.ksize,
                 'alpha': self.alpha,
                 'id': self.node.id,
                 'neighbors': self.bootstrappableNeighbors() }
        with open(fname, 'w') as f:
            pickle.dump(data, f)

    @classmethod
    def loadState(self, fname):
        """
        Load the state of this node (the alpha/ksize/id/immediate neighbors)
        from a cache file with the given fname.
        """        
        with open(fname, 'r') as f:
            data = pickle.load(f)
        s = Server(data['ksize'], data['alpha'], data['id'])
        if len(data['neighbors']) > 0:
            s.bootstrap(data['neighbors'])
        return s

    def saveStateRegularly(self, fname, frequency=600):
        """
        Save the state of node with a given regularity to the given
        filename.
        
        @param fname: File to save retularly to
        @param frequencey: Frequency in seconds that the state
        should be saved.  By default, 10 minutes.
        """
        loop = LoopingCall(self.saveState, fname)
        loop.start(frequency)
        return loop
Example #6
0
class Server(object):
    """
    High level view of a node instance.  This is the object that should be created
    to start listening as an active node on the network.
    """
    def __init__(self, ksize=20, alpha=3, id=None, storage=None):
        """
        Create a server instance.  This will start listening on the given port.

        Args:
            ksize (int): The k parameter from the paper
            alpha (int): The alpha parameter from the paper
            id: The id for this node on the network.
            storage: An instance that implements :interface:`~kademlia.storage.IStorage`
        """
        self.ksize = ksize
        self.alpha = alpha
        self.log = Logger(system=self)
        self.storage = storage or ForgetfulStorage()
        self.node = Node(id or digest(random.getrandbits(255)))
        print(random.getrandbits(255))
        self.protocol = KademliaProtocol(self.node, self.storage, ksize)
        self.refreshLoop = LoopingCall(self.refreshTable).start(3600)

    def listen(self, port, interface=""):
        """
        Start listening on the given port.

        This is the same as calling::

            reactor.listenUDP(port, server.protocol)

        Provide interface="::" to accept ipv6 address
        """
        return reactor.listenUDP(port, self.protocol, interface)

    def refreshTable(self):
        """
        Refresh buckets that haven't had any lookups in the last hour
        (per section 2.3 of the paper).
        """
        ds = []
        for id in self.protocol.getRefreshIDs():
            node = Node(id)
            nearest = self.protocol.router.findNeighbors(node, self.alpha)
            spider = NodeSpiderCrawl(self.protocol, node, nearest, self.ksize,
                                     self.alpha)
            ds.append(spider.find())

        def republishKeys(_):
            ds = []
            # Republish keys older than one hour
            for dkey, value in self.storage.iteritemsOlderThan(3600):
                ds.append(self.digest_set(dkey, value))
            return defer.gatherResults(ds)

        return defer.gatherResults(ds).addCallback(republishKeys)

    def bootstrappableNeighbors(self):
        """
        Get a :class:`list` of (ip, port) :class:`tuple` pairs suitable for use as an argument
        to the bootstrap method.

        The server should have been bootstrapped
        already - this is just a utility for getting some neighbors and then
        storing them if this server is going down for a while.  When it comes
        back up, the list of nodes can be used to bootstrap.
        """
        neighbors = self.protocol.router.findNeighbors(self.node)
        return [tuple(n)[-2:] for n in neighbors]

    def bootstrap(self, addrs):
        """
        Bootstrap the server by connecting to other known nodes in the network.

        Args:
            addrs: A `list` of (ip, port) `tuple` pairs.  Note that only IP addresses
                   are acceptable - hostnames will cause an error.
        """
        # if the transport hasn't been initialized yet, wait a second
        if self.protocol.transport is None:
            return task.deferLater(reactor, 1, self.bootstrap, addrs)

        def initTable(results):
            nodes = []
            for addr, result in results.items():
                if result[0]:
                    nodes.append(Node(result[1], addr[0], addr[1]))
            spider = NodeSpiderCrawl(self.protocol, self.node, nodes,
                                     self.ksize, self.alpha)
            return spider.find()

        ds = {}
        for addr in addrs:
            ds[addr] = self.protocol.ping(addr, self.node.id)
        return deferredDict(ds).addCallback(initTable)

    def inetVisibleIP(self):
        """
        Get the internet visible IP's of this node as other nodes see it.

        Returns:
            A `list` of IP's.  If no one can be contacted, then the `list` will be empty.
        """
        def handle(results):
            ips = [result[1][0] for result in results if result[0]]
            self.log.debug("other nodes think our ip is %s" % str(ips))
            return ips

        ds = []
        for neighbor in self.bootstrappableNeighbors():
            ds.append(self.protocol.stun(neighbor))
        return defer.gatherResults(ds).addCallback(handle)

    def get(self, key):
        """
        Get a key if the network has it.

        Returns:
            :class:`None` if not found, the value otherwise.
        """
        dkey = digest(key)
        # if this node has it, return it
        if self.storage.get(dkey) is not None:
            return defer.succeed(self.storage.get(dkey))
        node = Node(dkey)
        nearest = self.protocol.router.findNeighbors(node)
        if len(nearest) == 0:
            self.log.warning("There are no known neighbors to get key %s" %
                             key)
            return defer.succeed(None)
        spider = ValueSpiderCrawl(self.protocol, node, nearest, self.ksize,
                                  self.alpha)
        return spider.find()

    def set(self, key, value):
        """
        Set the given key to the given value in the network.
        """
        self.log.debug("setting '%s' = '%s' on network" % (key, value))
        dkey = digest(key)
        return self.digest_set(dkey, value)

    def digest_set(self, dkey, value):
        """
        Set the given SHA1 digest key to the given value in the network.
        """
        node = Node(dkey)
        # this is useful for debugging messages
        hkey = binascii.hexlify(dkey)

        def store(nodes):
            self.log.info("setting '%s' on %s" % (hkey, map(str, nodes)))
            # if this node is close too, then store here as well
            if self.node.distanceTo(node) < max(
                [n.distanceTo(node) for n in nodes]):
                self.storage[dkey] = value
            ds = [self.protocol.callStore(n, dkey, value) for n in nodes]
            return defer.DeferredList(ds).addCallback(self._anyRespondSuccess)

        nearest = self.protocol.router.findNeighbors(node)
        if len(nearest) == 0:
            self.log.warning("There are no known neighbors to set key %s" %
                             hkey)
            return defer.succeed(False)
        spider = NodeSpiderCrawl(self.protocol, node, nearest, self.ksize,
                                 self.alpha)
        return spider.find().addCallback(store)

    def _anyRespondSuccess(self, responses):
        """
        Given the result of a DeferredList of calls to peers, ensure that at least
        one of them was contacted and responded with a Truthy result.
        """
        for deferSuccess, result in responses:
            peerReached, peerResponse = result
            if deferSuccess and peerReached and peerResponse:
                return True
        return False

    def saveState(self, fname):
        """
        Save the state of this node (the alpha/ksize/id/immediate neighbors)
        to a cache file with the given fname.
        """
        data = {
            'ksize': self.ksize,
            'alpha': self.alpha,
            'id': self.node.id,
            'neighbors': self.bootstrappableNeighbors()
        }
        if len(data['neighbors']) == 0:
            self.log.warning("No known neighbors, so not writing to cache.")
            return
        with open(fname, 'w') as f:
            pickle.dump(data, f)

    @classmethod
    def loadState(self, fname):
        """
        Load the state of this node (the alpha/ksize/id/immediate neighbors)
        from a cache file with the given fname.
        """
        with open(fname, 'r') as f:
            data = pickle.load(f)
        s = Server(data['ksize'], data['alpha'], data['id'])
        if len(data['neighbors']) > 0:
            s.bootstrap(data['neighbors'])
        return s

    def saveStateRegularly(self, fname, frequency=600):
        """
        Save the state of node with a given regularity to the given
        filename.

        Args:
            fname: File name to save retularly to
            frequencey: Frequency in seconds that the state should be saved.
                        By default, 10 minutes.
        """
        loop = LoopingCall(self.saveState, fname)
        loop.start(frequency)
        return loop
Example #7
0
class KademliaProtocol(RPCProtocol):
    def __init__(self, sourceNode, storage, ksize):
        RPCProtocol.__init__(self)
        self.router = RoutingTable(self, ksize, sourceNode)
        self.storage = storage
        self.sourceNode = sourceNode
        self.log = Logger(system=self)

    def getRefreshIDs(self):
        """
        Get ids to search for to keep old buckets up to date.
        """
        ids = []
        for bucket in self.router.getLonelyBuckets():
            ids.append(random.randint(*bucket.range))
        return ids

    def rpc_stun(self, sender):
        return sender

    def rpc_ping(self, sender, nodeid):
        source = Node(nodeid, sender[0], sender[1])
        self.router.addContact(source)
        return self.sourceNode.id

    def rpc_store(self, sender, nodeid, key, value):
        source = Node(nodeid, sender[0], sender[1])
        self.router.addContact(source)
        self.log.debug("got a store request from %s, storing value" %
                       str(sender))
        self.storage[key] = value
        return True

    def rpc_find_node(self, sender, nodeid, key):
        self.log.info("finding neighbors of %i in local table" %
                      long(nodeid.encode('hex'), 16))
        source = Node(nodeid, sender[0], sender[1])
        self.router.addContact(source)
        node = Node(key)
        return map(tuple, self.router.findNeighbors(node, exclude=source))

    def rpc_find_value(self, sender, nodeid, key):
        source = Node(nodeid, sender[0], sender[1])
        self.router.addContact(source)
        value = self.storage.get(key, None)
        if value is None:
            return self.rpc_find_node(sender, nodeid, key)
        return {'value': value}

    def callFindNode(self, nodeToAsk, nodeToFind):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.find_node(address, self.sourceNode.id, nodeToFind.id)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callFindValue(self, nodeToAsk, nodeToFind):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.find_value(address, self.sourceNode.id, nodeToFind.id)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callPing(self, nodeToAsk):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.ping(address, self.sourceNode.id)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callStore(self, nodeToAsk, key, value):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.store(address, self.sourceNode.id, key, value)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def transferKeyValues(self, node):
        """
        Given a new node, send it all the keys/values it should be storing.

        @param node: A new node that just joined (or that we just found out
        about).

        Process:
        For each key in storage, get k closest nodes.  If newnode is closer
        than the furtherst in that list, and the node for this server
        is closer than the closest in that list, then store the key/value
        on the new node (per section 2.5 of the paper)
        """
        ds = []
        for key, value in self.storage.iteritems():
            keynode = Node(digest(key))
            neighbors = self.router.findNeighbors(keynode)
            if len(neighbors) > 0:
                newNodeClose = node.distanceTo(
                    keynode) < neighbors[-1].distanceTo(keynode)
                thisNodeClosest = self.sourceNode.distanceTo(
                    keynode) < neighbors[0].distanceTo(keynode)
            if len(neighbors) == 0 or (newNodeClose and thisNodeClosest):
                ds.append(self.callStore(node, key, value))
        return defer.gatherResults(ds)

    def handleCallResponse(self, result, node):
        """
        If we get a response, add the node to the routing table.  If
        we get no response, make sure it's removed from the routing table.
        """
        if result[0]:
            self.log.info("got response from %s, adding to router" % node)
            self.router.addContact(node)
            if self.router.isNewNode(node):
                self.transferKeyValues(node)
        else:
            self.log.debug("no response from %s, removing from router" % node)
            self.router.removeContact(node)
        return result
Example #8
0
class TalosKademliaProtocol(TalosRPCProtocol):
    """
    New protocol for the talos storage, base protocol from bmuller's implementation
    """
    def __init__(self,
                 sourceNode,
                 storage,
                 ksize,
                 talos_vc=TalosVCRestClient()):
        TalosRPCProtocol.__init__(self)
        self.router = TalosKademliaRoutingTable(self, ksize, sourceNode)
        self.storage = storage
        self.sourceNode = sourceNode
        self.log = Logger(system=self)
        self.talos_vc = talos_vc
        self.http_client = None

    def getRefreshIDs(self):
        """
        Get ids to search for to keep old buckets up to date.
        """
        ids = []
        for bucket in self.router.getLonelyBuckets():
            ids.append(random.randint(*bucket.range))
        return ids

    def rpc_stun(self, sender):
        return sender

    def rpc_ping(self, sender, nodeid):
        source = Node(nodeid, sender[0], sender[1])
        self.welcomeIfNewNode(source)
        return self.sourceNode.id

    def rpc_store(self, sender, nodeid, key, value):
        source = Node(nodeid, sender[0], sender[1])
        time_keeper = TimeKeeper()
        total_time_id = time_keeper.start_clock_unique()

        time_keeper.start_clock()
        self.welcomeIfNewNode(source)
        time_keeper.stop_clock(ENTRY_TIME_WELCOME_NODE)

        self.log.debug("got a store request from %s, storing value" %
                       str(sender))
        try:

            chunk = CloudChunk.decode(value)

            if not digest(chunk.key) == key:
                return {'error': 'key missmatch'}

            def handle_policy(policy):
                time_keeper.stop_clock(ENTRY_FETCH_POLICY)

                # Hack no chunk id given -> no key checks, key is in the encoded chunk
                id = time_keeper.start_clock_unique()
                self.storage.store_check_chunk(chunk,
                                               None,
                                               policy,
                                               time_keeper=time_keeper)
                time_keeper.stop_clock_unique(ENTRY_STORE_CHECK, id)

                time_keeper.stop_clock_unique(ENTRY_TOTAL_STORE_LOCAL,
                                              total_time_id)
                self.log.debug("%s %s %s" % (BENCH_TAG, TYPE_STORE_CHUNK_LOCAL,
                                             time_keeper.get_summary()))
                return {'value': 'ok'}

            time_keeper.start_clock()
            return self.talos_vc.get_policy_with_txid(
                chunk.get_tag_hex()).addCallback(handle_policy)
        except InvalidChunkError as e:
            return {'error': e.value}
        except TalosVCRestClientError:
            return {'error': "No policy found"}

    def rpc_find_node(self, sender, nodeid, key):
        self.log.info("finding neighbors of %i in local table" %
                      long(nodeid.encode('hex'), 16))
        source = Node(nodeid, sender[0], sender[1])
        self.welcomeIfNewNode(source)
        node = Node(key)
        return map(tuple, self.router.findNeighbors(node, exclude=source))

    def rpc_find_value(self, sender, nodeid, key, chunk_key):
        source = Node(nodeid, sender[0], sender[1])
        self.welcomeIfNewNode(source)
        if self.storage.has_value(chunk_key):
            try:
                myaddress = self.transport.getHost()
                return {'value': "%s:%d" % (myaddress.host, myaddress.port)}
            except InvalidQueryToken as e:
                self.log.info("Invalid query token received %s" % (e.value, ))
                return {'error': e.value}
        else:
            return self.rpc_find_node(sender, nodeid, key)

    def callFindNode(self, nodeToAsk, nodeToFind):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.find_node(address, self.sourceNode.id, nodeToFind.id)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callFindValue(self, nodeToAsk, nodeToFind, chunk_key):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.find_value(address, self.sourceNode.id, nodeToFind.id,
                            chunk_key)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callPing(self, nodeToAsk):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.ping(address, self.sourceNode.id)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callStore(self, nodeToAsk, key, value):
        address = (nodeToAsk.ip, nodeToAsk.port)
        time_keeper = TimeKeeper()
        id = time_keeper.start_clock_unique()
        if len(value) < MAX_UDP_SIZE:
            d = self.store(address, self.sourceNode.id, key, value)
        else:
            d = self.http_client.call_store_large_chunk(nodeToAsk, key, value)
        return d.addCallback(self.handleTimedCallResponse, nodeToAsk,
                             time_keeper, id, ENTRY_STORE_ONE_NODE)

    def welcomeIfNewNode(self, node):
        """
        Given a new node, send it all the keys/values it should be storing,
        then add it to the routing table.

        @param node: A new node that just joined (or that we just found out
        about).

        Process:
        For each key in storage, get k closest nodes.  If newnode is closer
        than the furtherst in that list, and the node for this server
        is closer than the closest in that list, then store the key/value
        on the new node (per section 2.5 of the paper)
        """
        def perform_stores():
            ds = []
            for key, value in self.storage.iteritems():
                keynode = Node(digest(key))
                neighbors = self.router.findNeighbors(keynode)
                if len(neighbors) > 0:
                    newNodeClose = node.distanceTo(
                        keynode) < neighbors[-1].distanceTo(keynode)
                    thisNodeClosest = self.sourceNode.distanceTo(
                        keynode) < neighbors[0].distanceTo(keynode)
                if len(neighbors) == 0 or (newNodeClose and thisNodeClosest):
                    ds.append(self.callStore(node, digest(key), value))

        if self.router.isNewNode(node):
            self.log.info("Welcoming new node %s" % node)
            ds = []
            threads.deferToThread(perform_stores)
            self.router.addContact(node)
            return defer.gatherResults(ds)

    def handleCallResponse(self, result, node):
        """
        If we get a response, add the node to the routing table.  If
        we get no response, make sure it's removed from the routing table.
        """
        if result[0]:
            self.log.info("got response from %s, adding to router" % node)
            self.welcomeIfNewNode(node)
        else:
            self.log.debug("no response from %s, removing from router" % node)
            self.router.removeContact(node)
        return result

    def handleTimedCallResponse(self, result, node, time_keeper, id, name):
        """
        If we get a response, add the node to the routing table.  If
        we get no response, make sure it's removed from the routing table.
        """
        time_keeper.stop_clock_unique(name, id)
        self.log.debug(
            "%s %s %s " %
            (BENCH_TAG, TYPE_STORE_CHUNK_REMOTE, time_keeper.get_summary()))

        if result[0]:
            self.log.info("got response from %s, adding to router" % node)
            self.welcomeIfNewNode(node)
        else:
            self.log.debug("no response from %s, removing from router" % node)
            self.router.removeContact(node)
        return result
Example #9
0
class StoreLargeChunk(Resource):
    allowedMethods = ('POST', )

    def __init__(self, storage, rpc_protocol, talos_vc=TalosVCRestClient()):
        Resource.__init__(self)
        self.storage = storage
        self.log = Logger(system=self)
        self.talos_vc = talos_vc
        self.rpc_protocol = rpc_protocol

    def getChild(self, path, request):
        return self

    def render_POST(self, request):
        if len(request.prepath) < 4:
            request.setResponseCode(400)
            return json.dumps({'error': "Illegal URL"})
        try:
            time_keeper = TimeKeeper()
            total_time_id = time_keeper.start_clock_unique()

            nodeid = unhexlify(request.prepath[1])
            source_ip = request.client.host
            source_port = int(request.prepath[2])
            kad_key = unhexlify(request.prepath[3])

            source = Node(nodeid, source_ip, source_port)

            time_keeper.start_clock()
            self.rpc_protocol.welcomeIfNewNode(source)
            time_keeper.stop_clock(ENTRY_TIME_WELCOME_NODE)

            encoded_chunk = request.content.read()

            chunk = CloudChunk.decode(encoded_chunk)

            if not digest(chunk.key) == kad_key:
                request.setResponseCode(400)
                return json.dumps({'error': "key missmatch"})

            def handle_policy(policy):
                time_keeper.stop_clock(ENTRY_FETCH_POLICY)

                id = time_keeper.start_clock_unique()
                self.storage.store_check_chunk(chunk,
                                               None,
                                               policy,
                                               time_keeper=time_keeper)
                time_keeper.stop_clock_unique(ENTRY_STORE_CHECK, id)

                time_keeper.stop_clock_unique(ENTRY_TOTAL_STORE_LOCAL,
                                              total_time_id)
                self.log.debug("%s %s %s" % (BENCH_TAG, TYPE_STORE_CHUNK_LOCAL,
                                             time_keeper.get_summary()))
                request.write(json.dumps({'value': "ok"}))
                request.finish()

            time_keeper.start_clock()
            self.talos_vc.get_policy_with_txid(
                chunk.get_tag_hex()).addCallback(handle_policy)
            return NOT_DONE_YET
        except InvalidChunkError as e:
            request.setResponseCode(400)
            return json.dumps({'error': e.value})
        except TalosVCRestClientError:
            request.setResponseCode(400)
            return "ERROR: No policy found"
        except:
            request.setResponseCode(400)
            return json.dumps({'error': "Error occured"})
Example #10
0
class QueryChunk(Resource):
    allowedMethods = ('GET', 'POST')

    def __init__(self,
                 storage,
                 talos_vc=TalosVCRestClient(),
                 max_nonce_cache=1000,
                 nonce_ttl=10):
        Resource.__init__(self)
        self.storage = storage
        self.log = Logger(system=self)
        self.talos_vc = talos_vc
        self.nonce_cache = TTLCache(max_nonce_cache, nonce_ttl)
        self.refreshLoop = LoopingCall(self.nonce_cache.expire).start(3600)
        self.sem = Semaphore(1)

    def render_GET(self, request):
        nonce = os.urandom(16)
        self.nonce_cache[nonce] = True
        return nonce

    def _check_cache(self, nonce):
        self.sem.acquire(True)
        try:
            ok = self.nonce_cache[nonce]
            if ok:
                self.nonce_cache[nonce] = False
            return ok
        except KeyError:
            return False
        finally:
            self.sem.release()

    def render_POST(self, request):
        msg = json.loads(request.content.read())
        timekeeper = TimeKeeper()
        total_time_id = timekeeper.start_clock_unique()
        try:
            timekeeper.start_clock()
            token = get_and_check_query_token(msg)
            check_query_token_valid(token)
            timekeeper.stop_clock(ENTRY_CHECK_TOKEN_VALID)

            # Check nonce ok
            if not self._check_cache(token.nonce):
                raise InvalidQueryToken("Nonce not valid")

            def handle_policy(policy):
                timekeeper.stop_clock(ENTRY_FETCH_POLICY)
                if policy is None:
                    request.setResponseCode(400)
                    request.write("No Policy Found")
                    request.finish()
                # check policy for correctness
                id = timekeeper.start_clock_unique()
                chunk = self.storage.get_check_chunk(token.chunk_key,
                                                     token.pubkey,
                                                     policy,
                                                     time_keeper=timekeeper)
                timekeeper.stop_clock_unique(ENTRY_GET_AND_CHECK, id)
                timekeeper.stop_clock_unique(ENTRY_TOTAL_LOCAL_QUERY,
                                             total_time_id)

                self.log.debug("%s %s %s" % (BENCH_TAG, TYPE_QUERY_CHUNK_LOCAL,
                                             timekeeper.get_summary()))
                request.write(chunk.encode())
                request.finish()

            timekeeper.start_clock()
            self.talos_vc.get_policy(token.owner,
                                     token.streamid).addCallback(handle_policy)
            return NOT_DONE_YET
        except InvalidQueryToken:
            request.setResponseCode(400)
            return "ERROR: token verification failure"
        except TalosVCRestClientError:
            request.setResponseCode(400)
            return "ERROR: No policy found"
        except:
            request.setResponseCode(400)
            return "ERROR: error occured"
Example #11
0
class NetworkInterface (object):

    # Create a NetworkInterface object to accomplish all network related tasks
    def __init__(self, appDeployer, uuid):
        self._connected = False
        self._app_deployer = appDeployer

        # optional...
        self._number_of_nodes = 0
        self._list_of_nodes =[] 
        
        # logging capabilities
        self._log = Logger(system=self)

        # HERE--> Implementation specific node instanciation
        from kademlia.network import Server
        self._node = Server()
        self._node.log.level = 4
        # END OF SECTION 

    def bootStrapDone(self, server):
        #contacts = self._node.inetVisibleIP()
        print "BOOOOTTTT STAPPP IT"

    def retrieveContacts(self):
        """
        NEED TO FIND A WAY TO RETRIEVE THE LIST OF NEIGHBORS !!!
        
        """
        # !!! DOES EXACTLY THE SAME AS bootstrappableNeighbors !!!
        for bucket in self._node.protocol.router.buckets:
            print bucket.getNodes()
        
        # !!! bootstrappableNeighbors returns only the list of neighbors that you provided as !!!
        # !!! a bootstrap list, that are also online !!!
        neighbors =  self._node.bootstrappableNeighbors()
        
        print neighbors
        return neighbors

    def connect(self,fromPort,toPort,ip='127.0.0.1'):
        self._log.debug('Connecting...')
        #print "in connect ... "  
        #print "now listening on port: ",fromPort
        self._node.listen(fromPort)
        return self._node.bootstrap([(ip,toPort)]).addCallback(self.bootStrapDone)
            
    # This function is used to set a value in the DHT
    def setDone(self,result):
        print result
        print "set is done"
        deferred = Deferred()
        return deferred

    def set(self, key, value):

        def _processKey(result, key, values):
            print result, key, values
            deferred = Deferred()
            # upon recovering the value of the key
            if result == None:
                deferred = self._node.set(key, values)
                return deferred 
                #.addCallback(self.setDone)
            else:
                for value in values:
                    if value not in result: 
                        # append + publish
                        result.append(value)
                    else:
                        self._log.info("Value is already in the corresponding key.")
                deferred = self._node.set(key, result)
            return deferred
            

        # Only application deployers are allowed to write to the DHT.
        if self._app_deployer != False: 
            deferred = Deferred()           
            # Two possible keys are allowed to be written to, the template key and their respective application key
            if ('template' == key or self._uuid == key) and key != None:
                # HERE --> Implementation Specific Code
                print  " :::  ", self,  " ::: ", key, " ::: ", value, " <----------------------------" 
                # if writing to the template, retrieve the value first then append to it if necessary
                if key == 'template':

                    deferred = self._node.get(key)
                    deferred.addCallback(_processKey, key, value)
                    return deferred
                    #self._node.set(key, value).addCallback(self.setDone)
                # END OF SECTION

        # Not Allowed to write to the DHT.
        else:
            self._log.info("Only application deployers are allowed to write values into the DHT!")
            

    def done(self,result):
        print "self: ", self
        print "Key result:", result

    def get(self,result, key):
        # HERE --> Implementation Specific Code
        print result, " :::  ", self,  " ::: ", key, " <----------------------------" 
        deferred = self._node.get(key)
        deferred.addCallback(self.done)
        return deferred
Example #12
0
class KademliaProtocol(RPCProtocol):
    def __init__(self, sourceNode, storage, ksize):
        RPCProtocol.__init__(self)
        self.router = RoutingTable(self, ksize, sourceNode)
        self.storage = storage
        self.sourceNode = sourceNode
        self.log = Logger(system=self)
        self.messages = []

    def getRefreshIDs(self):
        """
        Get ids to search for to keep old buckets up to date.
        """
        ids = []
        for bucket in self.router.getLonelyBuckets():
            ids.append(random.randint(*bucket.range))
        return ids

    def getMessages(self):
        if len(self.messages) == 0:
            return None

        newList = []
        while len(self.messages) > 0:
            newList.append(self.messages.pop(0))
        return newList

    def rpc_stun(self, sender):
        return sender

    def rpc_ping(self, sender, nodeid):
        source = Node(nodeid, sender[0], sender[1])
        self.router.addContact(source)
        return self.sourceNode.id

    def rpc_store(self, sender, nodeid, key, value):
        source = Node(nodeid, sender[0], sender[1])
        self.router.addContact(source)
        #Check if the timestamp of any existing value is larger than the new one.
        existingValue = self.storage.get(key, None)
        if existingValue:
            if existingValue[4] < value[4]:
                existingTimestamp = decodeTimestamp(existingValue[1], value[2])
            else:
                self.log.debug("Local val unencrypted is too small")
                return True
        if (not existingValue) or (existingTimestamp < decodeTimestamp(
                value[1], value[2])):
            self.log.debug("got a store request from %s, storing value" %
                           str(sender))
            self.storage[key] = value
            return True
        else:
            self.log.debug(
                "IGNORING a store request from %s, existing timestamp %s is larger than new %s"
                % (str(sender), str(existingTimestamp), str(newTimestamp)))
            return True

    def rpc_send(self, sender, message):
        self.log.info("Received message: \"" + message.strip("\n") +
                      "\" from address " + str(sender))
        self.messages.append(message)
        return True

    def rpc_find_node(self, sender, nodeid, key):
        self.log.info("finding neighbors of %i in local table" %
                      long(nodeid.encode('hex'), 16))
        source = Node(nodeid, sender[0], sender[1])
        self.router.addContact(source)
        node = Node(key)
        return map(tuple, self.router.findNeighbors(node, exclude=source))

    def rpc_find_value(self, sender, nodeid, key):
        source = Node(nodeid, sender[0], sender[1])
        self.router.addContact(source)
        value = self.storage.get(key, None)
        if value is None:
            return self.rpc_find_node(sender, nodeid, key)
        return {'value': value}

    def callFindNode(self, nodeToAsk, nodeToFind):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.find_node(address, self.sourceNode.id, nodeToFind.id)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callFindValue(self, nodeToAsk, nodeToFind):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.find_value(address, self.sourceNode.id, nodeToFind.id)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callPing(self, nodeToAsk):
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.ping(address, self.sourceNode.id)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callStore(self, nodeToAsk, key, value):
        self.log.debug("Storing on %s" % str(nodeToAsk))
        address = (nodeToAsk.ip, nodeToAsk.port)
        d = self.store(address, self.sourceNode.id, key, value)
        return d.addCallback(self.handleCallResponse, nodeToAsk)

    def callSend(self, message, addr, port):
        address = (addr, port)
        self.log.info("Sending message: \"" + message.strip("\n") +
                      "\" to address " + str(address))
        self.send(address, message)

    def transferKeyValues(self, node):
        """
        Given a new node, send it all the keys/values it should be storing.

        @param node: A new node that just joined (or that we just found out
        about).

        Process:
        For each key in storage, get k closest nodes.  If newnode is closer
        than the furtherst in that list, and the node for this server
        is closer than the closest in that list, then store the key/value
        on the new node (per section 2.5 of the paper)
        """
        ds = []
        for key, value in self.storage.iteritems():
            keynode = Node(digest(key))
            neighbors = self.router.findNeighbors(keynode)
            if len(neighbors) > 0:
                newNodeClose = node.distanceTo(
                    keynode) < neighbors[-1].distanceTo(keynode)
                thisNodeClosest = self.sourceNode.distanceTo(
                    keynode) < neighbors[0].distanceTo(keynode)
            if len(neighbors) == 0 or (newNodeClose and thisNodeClosest):
                ds.append(self.callStore(node, key, value))
        return defer.gatherResults(ds)

    def handleCallResponse(self, result, node):
        """
        If we get a response, add the node to the routing table.  If
        we get no response, make sure it's removed from the routing table.
        """
        if result[0]:
            self.log.debug("Result is %s" % str(result))
            self.log.info("got response from %s, adding to router" % node)
            self.router.addContact(node)
            if self.router.isNewNode(node):
                self.transferKeyValues(node)
        else:
            self.log.debug("no response from %s, removing from router" % node)
            self.router.removeContact(node)
        return result
Example #13
0
class NetworkInterface (object):

    # Create a NetworkInterface object to accomplish all network related tasks
    def __init__(self, appDeployer=False):
        self._connected = False
        self._app_deployer = appDeployer

        # optional...
        self._number_of_nodes = 0
        self._list_of_nodes =[] 
        
        # logging capabilities
        self._log = Logger(system=self)

        # HERE--> Implementation specific node instanciation
        from kademlia.network import Server
        self._node = Server()
        self._node.log.level = 4
        # END OF SECTION 

    def bootStrapDone(self, server):
        #contacts = self._node.inetVisibleIP()
        print "BOOOOTTTT STAPPP IT"

    def retrieveContacts(self):
        """
        NEED TO FIND A WAY TO RETRIEVE THE LIST OF NEIGHBORS !!!
        
        """
        # !!! DOES EXACTLY THE SAME AS bootstrappableNeighbors !!!
        for bucket in self._node.protocol.router.buckets:
            print bucket.getNodes()
        
        # !!! bootstrappableNeighbors returns only the list of neighbors that you provided as !!!
        # !!! a bootstrap list, that are also online !!!
        neighbors =  self._node.bootstrappableNeighbors()
        
        print neighbors
        return neighbors

    def connect(self,fromPort,toPort,ip='127.0.0.1'):
        self._log.debug('Connecting...')
        #print "in connect ... "  
        #print "now listening on port: ",fromPort
        self._node.listen(fromPort)
        return self._node.bootstrap([(ip,toPort)]).addCallback(self.bootStrapDone)
            
    # This function is used to set a value in the DHT
    def setDone(self,result):
        print result
        print "set is done"
        
    def set(self,result, key, value):
        # HERE --> Implementation Specific Code
        print result, " :::  ", self,  " ::: ", key, " ::: ", value, " <----------------------------" 
        self._node.set(key, value).addCallback(self.setDone)
        # END OF SECTION

    def done(self,result):
        print "self: ", self
        print "Key result:", result

    def get(self,result, key):
        # HERE --> Implementation Specific Code
        print result, " :::  ", self,  " ::: ", key, " <----------------------------" 
        self._node.get(key).addCallback(self.done)