X-Git-Url: https://git.mxchange.org/?p=quix0rs-apt-p2p.git;a=blobdiff_plain;f=apt_dht_Khashmir%2Fnode.py;h=49b8fe75a06923379d5bfecad8d86f083262a5aa;hp=5f11e51453acf06322b6d17485cb8489143eb5e4;hb=5ae15cb5cc24c1fd9a75ea7a8b416f733d1c79be;hpb=7b1b1619dadfe5dc3c0910c72054e247d54db2bd diff --git a/apt_dht_Khashmir/node.py b/apt_dht_Khashmir/node.py index 5f11e51..49b8fe7 100644 --- a/apt_dht_Khashmir/node.py +++ b/apt_dht_Khashmir/node.py @@ -1,54 +1,111 @@ ## Copyright 2002-2003 Andrew Loewenstern, All Rights Reserved # see LICENSE.txt for license information +"""Represents a node in the DHT. + +@type NULL_ID: C{string} +@var NULL_ID: the node ID to use until one is known +""" + from datetime import datetime, MINYEAR from types import InstanceType from twisted.trial import unittest import khash +from util import compact # magic id to use before we know a peer's id NULL_ID = 20 * '\0' class Node: - """encapsulate contact info""" - def __init__(self): + """Encapsulate a node's contact info. + + @ivar conn: the connection to the remote node (added externally) + @ivar table: the routing table (added externally) + @type fails: C{int} + @ivar fails: number of times this node has failed in a row + @type lastSeen: C{datetime.datetime} + @ivar lastSeen: the last time a response was received from this node + @type id: C{string} + @ivar id: the node's ID in the DHT + @type num: C{long} + @ivar num: the node's ID in number form + @type host: C{string} + @ivar host: the IP address of the node + @type port: C{int} + @ivar port: the port of the node + @type token: C{string} + @ivar token: the last received token from the node + @type num_values: C{int} + @ivar num_values: the number of values the node has for the key in the + currently executing action + """ + + def __init__(self, id, host = None, port = None): + """Initialize the node. + + @type id: C{string} or C{dictionary} + @param id: the node's ID in the DHT, or a dictionary containing the + node's id, host and port + @type host: C{string} + @param host: the IP address of the node + (optional, but must be specified if id is not a dictionary) + @type port: C{int} + @param port: the port of the node + (optional, but must be specified if id is not a dictionary) + """ self.fails = 0 self.lastSeen = datetime(MINYEAR, 1, 1) - self.id = self.host = self.port = '' - - def init(self, id, host, port): + + # Alternate method, init Node from dictionary + if isinstance(id, dict): + host = id['host'] + port = id['port'] + id = id['id'] + + assert isinstance(id, str) + assert isinstance(host, str) self.id = id self.num = khash.intify(id) self.host = host - self.port = port - self._senderDict = {'id': self.id, 'port' : self.port, 'host' : self.host} - return self - - def initWithDict(self, dict): - self._senderDict = dict - self.id = dict['id'] - self.num = khash.intify(self.id) - self.port = dict['port'] - self.host = dict['host'] - return self + self.port = int(port) + self.token = '' + self.num_values = 0 + self._contactInfo = None def updateLastSeen(self): + """Updates the last contact time of the node and resets the number of failures.""" self.lastSeen = datetime.now() self.fails = 0 + + def updateToken(self, token): + """Update the token for the node.""" + self.token = token + + def updateNumValues(self, num_values): + """Update how many values the node has in the current search for a value.""" + self.num_values = num_values def msgFailed(self): + """Log a failed attempt to contact this node. + + @rtype: C{int} + @return: the number of consecutive failures this node has + """ self.fails = self.fails + 1 return self.fails - def senderDict(self): - return self._senderDict + def contactInfo(self): + """Get the compact contact info for the node.""" + if self._contactInfo is None: + self._contactInfo = compact(self.id, self.host, self.port) + return self._contactInfo def __repr__(self): return `(self.id, self.host, self.port)` - ## these comparators let us bisect/index a list full of nodes with either a node or an int/long + #{ Comparators to bisect/index a list of nodes with either a node or a long def __lt__(self, a): if type(a) == InstanceType: a = a.num @@ -76,10 +133,11 @@ class Node: class TestNode(unittest.TestCase): + """Unit tests for the node implementation.""" def setUp(self): - self.node = Node().init(khash.newID(), 'localhost', 2002) + self.node = Node(khash.newID(), '127.0.0.1', 2002) def testUpdateLastSeen(self): t = self.node.lastSeen self.node.updateLastSeen() - assert t < self.node.lastSeen + self.failUnless(t < self.node.lastSeen) \ No newline at end of file