Example #1
0
class Communicator(object):
    '''
    Communicator talks to the client and receives commands from the client
    in terms of HTTP GET, PUT and DETELE verbs.
    Uses the HEAD verb as a control channel signaling the nodes to perform clean ups,
    remove dead peers from the key ring etc
    
    see utils package for sample standalone commands 
    '''

    base_file_path = os.path.abspath('.')

    #------------------------------------------------------------------------------ 
    # Logging setup
    #------------------------------------------------------------------------------ 
    logger = LogHelper.getLogger()

    def __init__(self, config):
        '''
        Constructor
        '''
        #=======================================================================
        # By this time, topology has configured itself based on the config file 
        # or the defaults
        #=======================================================================
        self.config = config
        self.topology = Topology(config)

        self.cache = Cache(reconstruct=self.config.local_reconstruct)

        self.logger.info('setup up communicator for node: ' + str(self.config.node_id) + '\n')


    def GET(self, key, origin='client'):

        if origin == 'client':
            self.logger.info("Request from client")
        else:
            if str(origin) == self.config.node_address:
                self.logger.error("Houston, we have a problem. Cycle detected. Have to fail...")
                return None

        node = self.topology.key_manager.get_node(key)

#        for node in node_gen:

        self.logger.info("Key requested: " + key)
        self.logger.info("Node responsible: " + node)

        if node == self.config.node_address:  #we are responsible for fetching this key from the cache
            return self.cache.fetch(key)
        else:                                   #tell our peer to handle this key
            return self.topology.instructPeer(node, 'GET', cherrypy.serving.request.path_info) #TODO change faux get/post to auto route
            #else:
            #===============================================================================
            # ideally there should be a for loop at the commented for node in node_gen where node_gen
            # is a generator that loops over the set of possible peers
            # need to figure out a way to update underlying datastructure over which the generator rotates
            # this is against the laws of a generator, so will have to think of some other clever trick.
            # This means the node we contacted was down. It was removed from the key ring
            # since node_gen is a generator, we will sping until we hit next node who will 
            # assume responsibility for this key               
            #===============================================================================


    def PUT(self, key, value, origin='client'):

        if origin == 'client':
            self.logger.info("Request from client")
        else:
            if str(origin) == self.config.node_address:
                self.logger.error("Houston, we have a problem. Cycle detected. Have to fail...")
                return None

        node = self.topology.key_manager.get_node(key)

#        for node in node_gen:

        if node == self.config.node_address:  #we are responsible for storing this key in the cache
            #also forward the request to mirrors
            print "mirroring"
            self.topology.mirror('PUT', cherrypy.serving.request.path_info)
            return self.cache.store(key, value)
        else:                                   #tell our peer to handle this key
            return self.topology.instructPeer(node, 'PUT', cherrypy.serving.request.path_info) #TODO change faux get/post to auto route
            #===============================================================================
            # This means the node we contacted was down. It was removed from the key ring
            # since node_gen is a generator, we will sping until we hit next node who will 
            # assume responsibility for this key               
            #===============================================================================



    def DELETE(self, key, origin='client'):

        if origin == 'client':
            self.logger.info("Request from Client")
        else:
            if str(origin) == self.config.node_address:
                self.logger.error("Houston, we have a problem. Cycle detected. Have to fail...")
                return None

        node = self.topology.key_manager.get_node(key)

        if node == self.config.node_address:  #we are responsible for deleting this key from the cache
            #also forward the request to mirrors
            self.topology.mirror('DELETE', cherrypy.serving.request.path_info)
            return self.cache.erase(key)
        else:                                   #tell our peer to handle this key
            return self.topology.instructPeer(node, 'DELETE', cherrypy.serving.request.path_info) #TODO change faux get/post to auto route


    def HEAD(self, key=None, value=None):
        print "head called"
        print key
        print value
        if key is None:
            #===================================================================
            # This is a simple heartbeat check, do nothing
            #===================================================================
            return "alive"
        if not isinstance(key, NoneType) and not isinstance(value, NoneType):
            if key == 'connect':
                self.topology.connect(value)
            elif key == 'dead':
                print '%s is dead' % value
                self.topology.key_manager.remove_node(value)
        elif not isinstance(key, NoneType) and key == 'reconstruct':
            #===================================================================
            # Need to work on this functionality
            #===================================================================
            self.cache.data_map = self.cache.diskCache.reconstruct() or LRUDict()


    #===============================================================================
    # Need to tell the CherryPy engine to expose this class as a web-servlet definition
    #===============================================================================
    exposed = True