bootstrap.py 5.41 KB
Newer Older
1
#
Grégory Wisniewski's avatar
Grégory Wisniewski committed
2
# Copyright (C) 2006-2010  Nexedi SA
3
#
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
#
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.
17

18
from neo import logging
19 20 21
from time import sleep

from neo.handler import EventHandler
22
from neo.protocol import Packets
23 24 25 26 27 28
from neo.util import dump
from neo.connection import ClientConnection

NO_SERVER = ('0.0.0.0', 0)

class BootstrapManager(EventHandler):
29
    """
30 31 32
    Manage the bootstrap stage, lookup for the primary master then connect to it
    """

33
    def __init__(self, app, name, node_type, uuid=None, server=NO_SERVER):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
34 35
        """
        Manage the bootstrap stage of a non-master node, it lookup for the
36
        primary master node, connect to it then returns when the master node
Grégory Wisniewski's avatar
Grégory Wisniewski committed
37 38
        is ready.
        """
39 40 41
        EventHandler.__init__(self, app)
        self.primary = None
        self.server = server
42
        self.node_type = node_type
43 44
        self.uuid = uuid
        self.name = name
45 46
        self.num_replicas = None
        self.num_partitions = None
47
        self.current = None
48 49

    def connectionCompleted(self, conn):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
50 51 52 53
        """
        Triggered when the network connection is successful.
        Now ask who's the primary.
        """
54
        EventHandler.connectionCompleted(self, conn)
55
        self.current.setRunning()
56
        conn.ask(Packets.AskPrimary())
57 58

    def connectionFailed(self, conn):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
59 60 61 62
        """
        Triggered when the network connection failed.
        Restart bootstrap.
        """
63 64 65
        EventHandler.connectionFailed(self, conn)
        self.current = None

66
    def connectionLost(self, conn, new_state):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
67 68 69 70
        """
        Triggered when an established network connection is lost.
        Restart bootstrap.
        """
71
        self.current.setTemporarilyDown()
72 73
        self.current = None

74
    def notReady(self, conn, message):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
75 76
        """
        The primary master send this message when it is still not ready to
77
        handle the client node.
Grégory Wisniewski's avatar
Grégory Wisniewski committed
78 79
        Close connection and restart.
        """
80 81 82 83
        # master are still electing on of them
        self.current = None
        conn.close()

84
    def answerPrimary(self, conn, primary_uuid, known_master_list):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
85 86 87 88 89
        """
        A master answer who's the primary. If it's another node, connect to it.
        If it's itself then the primary is successfully found, ask
        identification.
        """
90 91 92
        nm  = self.app.nm

        # Register new master nodes.
93
        for address, uuid in known_master_list:
94
            node = nm.getByAddress(address)
95
            if node is None:
96
                node = nm.createMaster(address=address)
97 98
            node.setUUID(uuid)

99
        self.primary = nm.getByUUID(primary_uuid)
100 101 102 103 104 105 106 107 108 109
        if self.primary is None or self.current is not self.primary:
            # three cases here:
            # - something goes wrong (unknown UUID)
            # - this master doesn't know who's the primary
            # - got the primary's uuid, so cut here
            self.current = None
            conn.close()
            return

        logging.info('connected to a primary master node')
110
        conn.ask(Packets.RequestIdentification(self.node_type,
111
                self.uuid, self.server, self.name))
112

113
    def acceptIdentification(self, conn, node_type,
114
           uuid, num_partitions, num_replicas, your_uuid):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
115 116 117
        """
        The primary master has accepted the node.
        """
118 119 120 121 122 123 124 125 126
        self.num_partitions = num_partitions
        self.num_replicas = num_replicas
        if self.uuid != your_uuid:
            # got an uuid from the primary master
            self.uuid = your_uuid
            logging.info('Got a new UUID : %s' % dump(self.uuid))
        conn.setUUID(uuid)

    def getPrimaryConnection(self, connector_handler):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
127 128 129 130
        """
        Primary lookup/connection process.
        Returns when the connection is made.
        """
131 132 133
        logging.info('connecting to a primary master node')
        em, nm = self.app.em, self.app.nm
        index = 0
134
        self.current = nm.getMasterList()[0]
135 136 137 138
        conn = None
        # retry until identified to the primary
        while self.primary is None or conn.getUUID() != self.primary.getUUID():
            if self.current is None:
139
                # conn closed
140 141
                conn = None
                # select a master
142
                master_list = nm.getMasterList()
143 144 145 146 147 148 149
                index = (index + 1) % len(master_list)
                self.current = master_list[index]
                if index == 0:
                    # tried all known masters, sleep a bit
                    sleep(1)
            if conn is None:
                # open the connection
150
                addr = self.current.getAddress()
151
                conn = ClientConnection(em, self, addr, connector_handler())
152
            # still processing
153
            em.poll(1)
154
        node = nm.getByUUID(conn.getUUID())
155 156 157 158
        return (node, conn, self.uuid, self.num_partitions, self.num_replicas)