def __init__(self):
     self.lock = ReadWriteLock()
     self.peers = dict()         # Contains a set of peers for each object type
     self.responses = dict()
     self.next_id = 0
     self.rand = random.Random()
     self.rand.seed()
 def __init__(self):
     self.lock = ReadWriteLock()
     self.peers = dict()  # Contains a set of peers for each object type
     self.responses = dict()
     self.next_id = 0
class NameServer(object):
    """Class that handles peers."""

    # The dictionary self.peers assigns a set to each object type
    # This set contains tuples that represent the peers of that type
    # The tuple is of the id of that peer and their address (id, addr)

    # So for example, self.peers could look like this:

    # obj_type (key)    | peers (entry)
    # ------------------+------------------------------------------------------
    # [obj0]            | [ (0, addr0), (3, addr3) ]
    # [obj1]            | [ (1, addr1), (4, addr4), (5, addr5) ]
    # [obj2]            | [ (2, addr2) ]

    def __init__(self):
        self.lock = ReadWriteLock()
        self.peers = dict()  # Contains a set of peers for each object type
        self.responses = dict()
        self.next_id = 0

    # Public methods

    def register(self, obj_type, address):
        self._check_all_alive(
            obj_type)  # Make sure everyone in our group is still alive

        address = tuple(address)  # The address might come in as a list.
        logging.debug("NameServer registering peer at {}".format(address))

        # We're making modifications to the NameServer's data
        self.lock.write_acquire()
        obj_hash = address  # Set the hash to the address (for now)
        obj_id = self.next_id
        self.next_id += 1
        t = (obj_id, obj_hash)
        self.lock.write_release()

        # We're adding the address to the group
        group = self._get_group(obj_type)
        self.lock.write_acquire(
        )  # Acquire the lock before we add ourselves to the group
        group.add(t)  # Add ourselves
        self.lock.write_release()  # Release the lock

        logging.info("NameServer done registering peer at {}".format(address))
        return t

    def unregister(self, obj_id, obj_type, obj_hash):
        logging.debug("NameServer unregistering peer at {}".format(
            tuple(obj_hash)))

        # Get the data necessary
        group = self._get_group(obj_type)
        t = (obj_id, tuple(obj_hash))

        # Remove from the group (if it exists)
        self.lock.write_acquire()
        if t in group:
            group.remove(t)
        else:
            logging.debug(
                "\nERR: Unregistering peer not registered!\n{}".format(
                    (obj_id, obj_type, obj_hash)))
        self.lock.write_release()
        logging.info("NameServer done unregistering peer at {}".format(
            tuple(obj_hash)))
        # This function doesn't stop until every peer has been checked.
        # BUT there's a peer out there waiting for this function to return
        # Before it can be unregistered.
        # This is very obnoxious.
        self._check_all_alive(
            obj_type)  # Make sure everyone in our group is still alive
        return "null"

    def get_peers(self, obj_type):
        return list(self._get_group(obj_type))

    def _get_group(self, obj_type):
        # Create our group if it doesn't already exist
        self.lock.write_acquire()
        if obj_type not in self.peers.keys():
            self.peers[obj_type] = set()
        self.lock.write_release()

        # Fetch and return the group
        self.lock.read_acquire()
        group = self.peers.get(obj_type)
        self.lock.read_release()
        return group

    def _check_all_alive(self, obj_type):
        logging.info("NameServer confirming connections to all peers" \
                 + " of type {}.".format(obj_type))
        group = copy.deepcopy(self._get_group(obj_type))
        for peer in group:
            self._check_alive(obj_type, peer)

    def _check_alive(self, obj_type, peer):
        logging.debug("NameServer confirming connection to peer {}.".format(
            peer[0]))
        if not self._is_alive(obj_type, peer, 5):
            t = peer
            group = self._get_group(obj_type)
            self.lock.write_acquire()
            logging.info("Removing peer {}.".format(t))
            group.remove(t)
            self.lock.write_release()

    def _is_alive(self, obj_type, peer, timeout=5):
        try:
            parent_conn, child_conn = multiprocessing.Pipe(duplex=False)
            p = multiprocessing.Process(target=self._get_line,
                                        args=(child_conn, peer, obj_type))
            p.daemon = True
            p.start()
            it_did_timeout = (not parent_conn.poll(timeout))
            if it_did_timeout:
                logging.info("Connection to peer {} timed out.".format(peer))
                return False
            else:
                parent_said_yes = parent_conn.recv()
                if not parent_said_yes:
                    logging.info(
                        "No connection to peer {} established.".format(peer))
                return parent_said_yes
        except:
            err = sys.exc_info()
            logging.debug(
                "NameServer encountered an error while spawning a process to check if peer {} is still alive:\n{}: {}"
                .format(peer, err[0], err[1]))
            return False

    def _get_line(self, conn, peer, obj_type):
        result = False
        try:
            expected = [peer[0], obj_type]
            response = Stub(peer[1]).isAlive()
            result = (response == expected)
            logging.debug(
                "NameServer received response {} from peer {}".format(
                    response, peer))
            if result is True:
                logging.debug("This was the expected response.")
            else:
                logging.debug(
                    "This was not the expected response.\n Expected response: {}"
                    .format(expected))
        except ConnectionRefusedError:
            logging.info("Peer {} refused connection".format(peer))
            result = False
        except:
            err = sys.exc_info()
            logging.debug(
                "NameServer encountered an error trying to check if peer {} is still alive:\n{}: {}"
                .format(peer, err[0], err[1]))
        finally:
            conn.send(result)
class NameServer(object):
    """Class that handles peers."""

    # The dictionary self.peers assigns a set to each object type
    # This set contains tuples that represent the peers of that type
    # The tuple is of the id of that peer and their address (id, addr)

    # So for example, self.peers could look like this:

    # obj_type (key)    | peers (entry)
    # ------------------+------------------------------------------------------
    # [obj0]            | [ (0, addr0), (3, addr3) ]
    # [obj1]            | [ (1, addr1), (4, addr4), (5, addr5) ]
    # [obj2]            | [ (2, addr2) ]

    def __init__(self):
        self.lock = ReadWriteLock()
        self.peers = dict()         # Contains a set of peers for each object type
        self.responses = dict()
        self.next_id = 0
        self.rand = random.Random()
        self.rand.seed()

    # Public methods

    def register(self, obj_type, address):
        self._check_all_alive(obj_type) # Make sure everyone in our group is still alive

        address = tuple(address)    # The address might come in as a list.
        logging.debug("NameServer registering peer at {}".format(address))

        # We're making modifications to the NameServer's data
        self.lock.write_acquire()
        obj_hash = address       # Set the hash to the address (for now)
        obj_id = self.next_id
        self.next_id += 1
        t = (obj_id, obj_hash)
        self.lock.write_release()

        # We're adding the address to the group
        group = self._get_group(obj_type)
        self.lock.write_acquire() # Acquire the lock before we add ourselves to the group
        group.add(t)              # Add ourselves
        self.lock.write_release() # Release the lock

        logging.info("NameServer done registering peer at {}".format(address))
        return t

    def unregister(self, obj_id, obj_type, obj_hash):
        logging.debug("NameServer unregistering peer at {}".format(tuple(obj_hash)))

        # Get the data necessary
        group = self._get_group(obj_type)
        t = (obj_id, tuple(obj_hash))

        # Remove from the group (if it exists)
        self.lock.write_acquire()
        if t in group:
            group.remove(t)
        else:
            logging.debug("\nERR: Unregistering peer not registered!\n{}"
                          .format((obj_id,obj_type,obj_hash)))
        self.lock.write_release()
        logging.info("NameServer done unregistering peer at {}".format(tuple(obj_hash)))
	# This function doesn't stop until every peer has been checked.
        # BUT there's a peer out there waiting for this function to return
        # Before it can be unregistered.
        # This is very obnoxious.
        self._check_all_alive(obj_type) # Make sure everyone in our group is still alive
        return "null"

    def get_peers(self, obj_type):
        return list(self._get_group(obj_type))

    def require_any(self, obj_type):
        

        all_matching_peers = self.get_peers(obj_type)
        idx = self.rand.randint(0,len(all_matching_peers)-1)
        print("require_any returning {} of {}".format(idx,len(all_matching_peers)))
        return all_matching_peers[idx][1] # Return only the address

    def require_object(self, server_type, server_id):
        server_type_list = self.get_peers(server_type)
        return server_type_list[server_id]
        
    def _get_group(self, obj_type):
        # Create our group if it doesn't already exist
        self.lock.write_acquire()
        if obj_type not in self.peers.keys():
            self.peers[obj_type] = set()
        self.lock.write_release()

        # Fetch and return the group
        self.lock.read_acquire()
        group = self.peers.get(obj_type)
        self.lock.read_release()
        return group

    def _check_all_alive(self, obj_type):
        logging.info("NameServer confirming connections to all peers" \
                 + " of type {}.".format(obj_type))
        group = copy.deepcopy(self._get_group(obj_type))
        for peer in group:
            self._check_alive(obj_type, peer)

    def _check_alive(self, obj_type, peer):
        logging.debug("NameServer confirming connection to peer {}.".format(peer[0]))
        if not self._is_alive(obj_type, peer, 5):
            t = peer
            group = self._get_group(obj_type)
            self.lock.write_acquire()
            logging.info("Removing peer {}.".format(t))
            group.remove(t)
            self.lock.write_release()

    def _is_alive(self, obj_type, peer, timeout=5):
        try:
            parent_conn, child_conn = multiprocessing.Pipe(duplex=False)
            p = multiprocessing.Process(
                target=self._get_line,
                args=(child_conn, peer, obj_type)
            )
            p.daemon = True
            p.start()
            it_did_timeout = (not parent_conn.poll(timeout))
            if it_did_timeout:
                logging.info("Connection to peer {} timed out.".format(peer))
                return False
            else:
                parent_said_yes = parent_conn.recv()
                if not parent_said_yes:
                    logging.info("No connection to peer {} established.".format(peer))
                return parent_said_yes
        except:
            err = sys.exc_info()
            logging.debug("NameServer encountered an error while spawning a process to check if peer {} is still alive:\n{}: {}"
                          .format(peer, err[0], err[1]))
            return False

    def _get_line(self, conn, peer, obj_type):
        result = False
        try:
            expected = [peer[0], obj_type]
            response = Stub(peer[1]).isAlive()
            result = (response == expected)
            logging.debug("NameServer received response {} from peer {}".format(response, peer))
            if result is True:
                logging.debug("This was the expected response.")
            else:
                logging.debug("This was not the expected response.\n Expected response: {}"
                              .format(expected))
        except ConnectionRefusedError:
            logging.info("Peer {} refused connection".format(peer))
            result = False
        except:
            err = sys.exc_info()
            logging.debug("NameServer encountered an error trying to check if peer {} is still alive:\n{}: {}"
                         .format(peer, err[0], err[1]))
        finally:
            conn.send(result)