election.py 8.06 KB
Newer Older
Aurel's avatar
Aurel committed
1
#
Grégory Wisniewski's avatar
Grégory Wisniewski committed
2
# Copyright (C) 2006-2010  Nexedi SA
3
#
Aurel's avatar
Aurel committed
4 5 6 7
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
8
#
Aurel's avatar
Aurel committed
9 10 11 12 13 14 15
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Aurel's avatar
Aurel committed
17

18
import neo.lib
19

20
from neo.lib.protocol import NodeTypes, NodeStates, Packets
Olivier Cros's avatar
Olivier Cros committed
21
from neo.lib.protocol import NotReadyError, ProtocolError, \
22 23 24 25
                              UnexpectedPacketError
from neo.lib.protocol import BrokenNodeDisallowedError
from neo.lib.exception import ElectionFailure
from neo.lib.util import dump
26
from . import MasterHandler
27

28
class ClientElectionHandler(MasterHandler):
29

30 31
    # FIXME: this packet is not allowed here, but handled in MasterHandler
    # a global handler review is required.
32
    def askPrimary(self, conn):
33 34
        raise UnexpectedPacketError, "askPrimary on server connection"

35 36
    def connectionStarted(self, conn):
        addr = conn.getAddress()
37 38 39
        # connection in progress
        self.app.unconnected_master_node_set.remove(addr)
        self.app.negotiating_master_node_set.add(addr)
40
        super(ClientElectionHandler, self).connectionStarted(conn)
41

42 43 44
    def connectionFailed(self, conn):
        addr = conn.getAddress()
        node = self.app.nm.getByAddress(addr)
45
        assert node is not None, (dump(self.app.uuid), addr)
46
        assert node.isUnknown(), (dump(self.app.uuid), node.whoSetState(),
47
          node.getState())
48 49 50
        # connection never success, node is still in unknown state
        self.app.negotiating_master_node_set.discard(addr)
        self.app.unconnected_master_node_set.add(addr)
51
        super(ClientElectionHandler, self).connectionFailed(conn)
52

53
    def connectionCompleted(self, conn):
54
        conn.ask(Packets.AskPrimary())
55
        super(ClientElectionHandler, self).connectionCompleted(conn)
56

57
    def connectionLost(self, conn, new_state):
58 59
        addr = conn.getAddress()
        self.app.negotiating_master_node_set.discard(addr)
60

61
    def acceptIdentification(self, conn, node_type,
62
            uuid, num_partitions, num_replicas, your_uuid):
63
        app = self.app
64
        node = app.nm.getByAddress(conn.getAddress())
65
        if node_type != NodeTypes.MASTER:
66
            # The peer is not a master node!
67
            neo.lib.logging.error('%r is not a master node', conn)
68 69 70
            app.nm.remove(node)
            conn.close()
            return
71

72 73 74
        if your_uuid != app.uuid:
            # uuid conflict happened, accept the new one and restart election
            app.uuid = your_uuid
75 76
            neo.lib.logging.info('UUID conflict, new UUID: %s',
                            dump(your_uuid))
77
            raise ElectionFailure, 'new uuid supplied'
78

79 80
        conn.setUUID(uuid)
        node.setUUID(uuid)
81

82 83 84 85 86
        if app.uuid < uuid:
            # I lost.
            app.primary = False

        app.negotiating_master_node_set.discard(conn.getAddress())
87

88
    def answerPrimary(self, conn, primary_uuid, known_master_list):
89 90
        app = self.app
        # Register new master nodes.
91 92
        for address, uuid in known_master_list:
            if app.server == address:
93 94
                # This is self.
                continue
95
            n = app.nm.getByAddress(address)
96 97
            if n is None:
                n = app.nm.createMaster(address=address)
98 99 100 101 102
            if uuid is not None:
                # If I don't know the UUID yet, believe what the peer
                # told me at the moment.
                if n.getUUID() is None or n.getUUID() != uuid:
                    n.setUUID(uuid)
103

104
        if primary_uuid is not None:
105 106 107 108 109 110
            # The primary master is defined.
            if app.primary_master_node is not None \
                    and app.primary_master_node.getUUID() != primary_uuid:
                # There are multiple primary master nodes. This is
                # dangerous.
                raise ElectionFailure, 'multiple primary master nodes'
111
            primary_node = app.nm.getByUUID(primary_uuid)
112 113 114
            if primary_node is None:
                # I don't know such a node. Probably this information
                # is old. So ignore it.
115 116
                neo.lib.logging.warning(
                                'received an unknown primary node UUID')
117
            else:
118 119 120 121 122
                # Whatever the situation is, I trust this master.
                app.primary = False
                app.primary_master_node = primary_node
                # Stop waiting for connections than primary master's to
                # complete to exit election phase ASAP.
123 124
                app.unconnected_master_node_set.clear()
                app.negotiating_master_node_set.clear()
125

126 127 128
        primary_node = app.primary_master_node
        if (primary_node is None or \
            conn.getAddress() == primary_node.getAddress()) and \
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
                not conn.isClosed():
            # Request a node identification.
            # There are 3 cases here:
            # - Peer doesn't know primary node
            #   We must ask its identification so we exchange our uuids, to
            #   know which of us is secondary.
            # - Peer knows primary node
            #   - He is the primary
            #     We must ask its identification, as part of the normal
            #     connection process
            #   - He is not the primary
            #     We don't need to ask its identification, as we will close
            #     this connection anyway (exiting election).
            # Also, connection can be closed by peer after he sent
            # AnswerPrimary if he finds the primary master before we
            # give him our UUID.
            # The connection gets closed before this message gets processed
            # because this message might have been queued, but connection
            # interruption takes effect as soon as received.
            conn.ask(Packets.RequestIdentification(
                NodeTypes.MASTER,
                app.uuid,
                app.server,
                app.name
            ))
154

155

156
class ServerElectionHandler(MasterHandler):
157

158
    def reelectPrimary(self, conn):
159 160
        raise ElectionFailure, 'reelection requested'

161
    def requestIdentification(self, conn, node_type,
162
                                        uuid, address, name):
163
        self.checkClusterName(name)
164
        app = self.app
165
        if node_type != NodeTypes.MASTER:
166
            neo.lib.logging.info('reject a connection from a non-master')
167
            raise NotReadyError
168
        node = app.nm.getByAddress(address)
169
        if node is None:
170
            node = app.nm.createMaster(address=address)
171 172
        # If this node is broken, reject it.
        if node.getUUID() == uuid:
173
            if node.isBroken():
174
                raise BrokenNodeDisallowedError
175 176

        # supplied another uuid in case of conflict
177
        while not app.isValidUUID(uuid, address):
178 179 180 181 182
            uuid = app.getNewUUID(node_type)

        node.setUUID(uuid)
        conn.setUUID(uuid)

183
        p = Packets.AcceptIdentification(
184 185 186 187
            NodeTypes.MASTER,
            app.uuid,
            app.pt.getPartitions(),
            app.pt.getReplicas(),
188 189
            uuid
        )
190
        conn.answer(p)
191

192
    def announcePrimary(self, conn):
193
        uuid = conn.getUUID()
194
        if uuid is None:
195
            raise ProtocolError('Not identified')
196 197 198 199
        app = self.app
        if app.primary:
            # I am also the primary... So restart the election.
            raise ElectionFailure, 'another primary arises'
200
        node = app.nm.getByUUID(uuid)
201 202
        app.primary = False
        app.primary_master_node = node
203 204
        app.unconnected_master_node_set.clear()
        app.negotiating_master_node_set.clear()
205
        neo.lib.logging.info('%s is the primary', node)
206