Exemple #1
0
    def __init__(self, participant, address=None, loop=None):
        """Create a new gossiper.

        @param address: Listen address if the gossiper will not be
            bound to a specific listen interface.
        @param address: C{str}
        """
        self.loop = loop or asyncio.get_event_loop()
        self.state = PeerState(participant, loop=self.loop)
        self._states = {}
        self._address = address
        self._scuttle = Scuttle(self._states, self.state)
        self._heart_beat_timer = LoopingCall(self._beat_heart, _loop=self.loop)
        self._gossip_timer = LoopingCall(self._gossip, _loop=self.loop)
        self.participant = participant
        self._seeds = []
        self._transport = None
        self.name = None
Exemple #2
0
    def __init__(self, participant, address=None, loop=None):
        """Create a new gossiper.

        @param address: Listen address if the gossiper will not be
            bound to a specific listen interface.
        @param address: C{str}
        """
        self.loop = loop or asyncio.get_event_loop()
        self.state = PeerState(participant, loop=self.loop)
        self._states = {}
        self._address = address
        self._scuttle = Scuttle(self._states, self.state)
        self._heart_beat_timer = LoopingCall(self._beat_heart, _loop=self.loop)
        self._gossip_timer = LoopingCall(self._gossip, _loop=self.loop)
        self.participant = participant
        self._seeds = []
        self._transport = None
        self.name = None
Exemple #3
0
class Gossiper(asyncio.DatagramProtocol, MutableMapping):

    def __init__(self, participant, address=None, loop=None):
        """Create a new gossiper.

        @param address: Listen address if the gossiper will not be
            bound to a specific listen interface.
        @param address: C{str}
        """
        self.loop = loop or asyncio.get_event_loop()
        self.state = PeerState(participant, loop=self.loop)
        self._states = {}
        self._address = address
        self._scuttle = Scuttle(self._states, self.state)
        self._heart_beat_timer = LoopingCall(self._beat_heart, _loop=self.loop)
        self._gossip_timer = LoopingCall(self._gossip, _loop=self.loop)
        self.participant = participant
        self._seeds = []
        self._transport = None
        self.name = None

    def _setup_state_for_peer(self, peer_name):
        """Setup state for a new peer."""
        self._states[peer_name] = PeerState(
            self.participant, name=peer_name, loop=self.loop)

    def seed(self, seeds):
        """Tell this gossiper that there are gossipers to
        be found at the given endpoints.

        @param seeds: a sequence of C{'ADDRESS:PORT'} strings.
        """
        self._seeds.extend(seeds)
        self._handle_new_peers(seeds)

    def _handle_new_peers(self, names):
        """Set up state for new peers."""
        for peer_name in names:
            if peer_name in self._states:
                continue
            self._setup_state_for_peer(peer_name)

    def _determine_endpoint(self):
        """Determine the IP address of this peer.

        @raises Exception: If it is not impossible to figure out the
            address.
        @return: a C{ADDRESS:PORT} string.
        """
        # Figure our our endpoint:
        address = self._transport.get_extra_info('sockname')
        if not self._address:
            self._address = address
            if self._address == '0.0.0.0':
                raise Exception("address not specified")
        return '%s:%d' % (address[0], address[1])

    def connection_made(self, transport):
        """Start protocol."""
        self._transport = transport
        self.name = self._determine_endpoint()
        self.state.set_name(self.name)
        self._states[self.name] = self.state
        self._heart_beat_timer.start(1)
        self._gossip_timer.start(0.5)
        self.participant.make_connection(self)

    def connection_lost(self, exc):
        """Stop protocol."""
        logging.info("Connection lost")
        self._gossip_timer.stop()
        self._heart_beat_timer.stop()

    def _beat_heart(self):
        """Beat heart of our own state."""
        self.state.beat_that_heart()

    def datagram_received(self, data, address):
        """Handle a received datagram."""
        self._handle_message(json.loads(data.decode()), address)

    def _gossip(self):
        """Initiate a round of gossiping."""
        #logging.debug("Starting some gossip...")
        live_peers = self.live_peers
        dead_peers = self.dead_peers
        if live_peers:
            self._gossip_with_peer(random.choice(live_peers))

        prob = len(dead_peers) / float(len(live_peers) + 1)
        if random.random() < prob:
            self._gossip_with_peer(random.choice(dead_peers))

        for state in self._states.values():
            if state.name != self.name:
                state.check_suspected()

    def _send_to(self, message, address):
        assert isinstance(message, dict)
        self._transport.sendto(json.dumps(message).encode(), address)

    def _gossip_with_peer(self, peer):
        """Send a gossip message to C{peer}."""
        request = {
            'type': 'request', 'digest': self._scuttle.digest()
        }
        self._send_to(request, _address_from_peer_name(peer.name))

    def _handle_message(self, message, address):
        """Handle an incoming message."""
        #logging.debug("Received: {0}".format(message))
        if message['type'] == 'request':
            self._handle_request(message, address)
        elif message['type'] == 'first-response':
            self._handle_first_response(message, address)
        elif message['type'] == 'second-response':
            self._handle_second_response(message, address)

    def _handle_request(self, message, address):
        """Handle an incoming gossip request."""
        deltas, requests, new_peers = self._scuttle.scuttle(
            message['digest'])
        self._handle_new_peers(new_peers)
        response = {
            'type': 'first-response',
            'digest': requests,
            'updates': deltas,
        }
        self._send_to(response, address)

    def _handle_first_response(self, message, address):
        """Handle the response to a request."""
        self._scuttle.update_known_state(message['updates'])
        response = {
            'type': 'second-response',
            'updates': self._scuttle.fetch_deltas(message['digest'])
        }
        self._send_to(response, address)

    def _handle_second_response(self, message, _):
        """Handle the ack of the response."""
        self._scuttle.update_known_state(message['updates'])

    @property
    def live_peers(self):
        """Property for all peers that we know is alive.

        The property holds a sequence L{PeerState}'s.
        """
        return [p for (n, p) in self._states.items()
                if p.alive and n != self.name]

    @property
    def dead_peers(self):
        """Property for all peers that we know is dead.

        The property holds a sequence L{PeerState}'s.
        """
        return [p for (n, p) in self._states.items()
                if not p.alive and n != self.name]

    # dict-like interface:

    def __getitem__(self, key):
        return self.state[key]

    def set(self, key, value):
        self.state[key] = value

    def __setitem__(self, key, value):
        self.set(key, value)

    def __delitem__(self, key):
        raise NotImplementedError()

    def __contains__(self, key):
        return key in self.state

    def has_key(self, key):
        return key in self.state

    def __len__(self):
        return len(self.state)

    def __iter__(self):
        return iter(self.state)
Exemple #4
0
class Gossiper(asyncio.DatagramProtocol, MutableMapping):
    def __init__(self, participant, address=None, loop=None):
        """Create a new gossiper.

        @param address: Listen address if the gossiper will not be
            bound to a specific listen interface.
        @param address: C{str}
        """
        self.loop = loop or asyncio.get_event_loop()
        self.state = PeerState(participant, loop=self.loop)
        self._states = {}
        self._address = address
        self._scuttle = Scuttle(self._states, self.state)
        self._heart_beat_timer = LoopingCall(self._beat_heart, _loop=self.loop)
        self._gossip_timer = LoopingCall(self._gossip, _loop=self.loop)
        self.participant = participant
        self._seeds = []
        self._transport = None
        self.name = None

    def _setup_state_for_peer(self, peer_name):
        """Setup state for a new peer."""
        self._states[peer_name] = PeerState(self.participant,
                                            name=peer_name,
                                            loop=self.loop)

    def seed(self, seeds):
        """Tell this gossiper that there are gossipers to
        be found at the given endpoints.

        @param seeds: a sequence of C{'ADDRESS:PORT'} strings.
        """
        self._seeds.extend(seeds)
        self._handle_new_peers(seeds)

    def _handle_new_peers(self, names):
        """Set up state for new peers."""
        for peer_name in names:
            if peer_name in self._states:
                continue
            self._setup_state_for_peer(peer_name)

    def _determine_endpoint(self):
        """Determine the IP address of this peer.

        @raises Exception: If it is not impossible to figure out the
            address.
        @return: a C{ADDRESS:PORT} string.
        """
        # Figure our our endpoint:
        address = self._transport.get_extra_info('sockname')
        if not self._address:
            self._address = address
            if self._address == '0.0.0.0':
                raise Exception("address not specified")
        return '%s:%d' % (address[0], address[1])

    def connection_made(self, transport):
        """Start protocol."""
        self._transport = transport
        self.name = self._determine_endpoint()
        self.state.set_name(self.name)
        self._states[self.name] = self.state
        self._heart_beat_timer.start(1)
        self._gossip_timer.start(0.5)
        self.participant.make_connection(self)

    def connection_lost(self, exc):
        """Stop protocol."""
        logging.info("Connection lost")
        self._gossip_timer.stop()
        self._heart_beat_timer.stop()

    def _beat_heart(self):
        """Beat heart of our own state."""
        self.state.beat_that_heart()

    def datagram_received(self, data, address):
        """Handle a received datagram."""
        self._handle_message(json.loads(data.decode()), address)

    def _gossip(self):
        """Initiate a round of gossiping."""
        #logging.debug("Starting some gossip...")
        live_peers = self.live_peers
        dead_peers = self.dead_peers
        if live_peers:
            self._gossip_with_peer(random.choice(live_peers))

        prob = len(dead_peers) / float(len(live_peers) + 1)
        if random.random() < prob:
            self._gossip_with_peer(random.choice(dead_peers))

        for state in self._states.values():
            if state.name != self.name:
                state.check_suspected()

    def _send_to(self, message, address):
        assert isinstance(message, dict)
        self._transport.sendto(json.dumps(message).encode(), address)

    def _gossip_with_peer(self, peer):
        """Send a gossip message to C{peer}."""
        request = {'type': 'request', 'digest': self._scuttle.digest()}
        self._send_to(request, _address_from_peer_name(peer.name))

    def _handle_message(self, message, address):
        """Handle an incoming message."""
        #logging.debug("Received: {0}".format(message))
        if message['type'] == 'request':
            self._handle_request(message, address)
        elif message['type'] == 'first-response':
            self._handle_first_response(message, address)
        elif message['type'] == 'second-response':
            self._handle_second_response(message, address)

    def _handle_request(self, message, address):
        """Handle an incoming gossip request."""
        deltas, requests, new_peers = self._scuttle.scuttle(message['digest'])
        self._handle_new_peers(new_peers)
        response = {
            'type': 'first-response',
            'digest': requests,
            'updates': deltas,
        }
        self._send_to(response, address)

    def _handle_first_response(self, message, address):
        """Handle the response to a request."""
        self._scuttle.update_known_state(message['updates'])
        response = {
            'type': 'second-response',
            'updates': self._scuttle.fetch_deltas(message['digest'])
        }
        self._send_to(response, address)

    def _handle_second_response(self, message, _):
        """Handle the ack of the response."""
        self._scuttle.update_known_state(message['updates'])

    @property
    def live_peers(self):
        """Property for all peers that we know is alive.

        The property holds a sequence L{PeerState}'s.
        """
        return [
            p for (n, p) in self._states.items() if p.alive and n != self.name
        ]

    @property
    def dead_peers(self):
        """Property for all peers that we know is dead.

        The property holds a sequence L{PeerState}'s.
        """
        return [
            p for (n, p) in self._states.items()
            if not p.alive and n != self.name
        ]

    # dict-like interface:

    def __getitem__(self, key):
        return self.state[key]

    def set(self, key, value):
        self.state[key] = value

    def __setitem__(self, key, value):
        self.set(key, value)

    def __delitem__(self, key):
        raise NotImplementedError()

    def __contains__(self, key):
        return key in self.state

    def has_key(self, key):
        return key in self.state

    def __len__(self):
        return len(self.state)

    def __iter__(self):
        return iter(self.state)