bootstrap.py 5.17 KB
Newer Older
1
#
2
# Copyright (C) 2006-2012  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
# 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
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

17
import neo
18 19
from time import sleep

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

NO_SERVER = ('0.0.0.0', 0)

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

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
        conn.close()

82
    def answerPrimary(self, conn, primary_uuid, known_master_list):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
83 84 85 86 87
        """
        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.
        """
88 89 90
        nm  = self.app.nm

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

97
        self.primary = nm.getByUUID(primary_uuid)
98 99 100 101 102 103 104 105
        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
            conn.close()
            return

106
        neo.lib.logging.info('connected to a primary master node')
107
        conn.ask(Packets.RequestIdentification(self.node_type,
108
                self.uuid, self.server, self.name))
109

110
    def acceptIdentification(self, conn, node_type,
111
           uuid, num_partitions, num_replicas, your_uuid):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
112 113 114
        """
        The primary master has accepted the node.
        """
115 116 117 118 119
        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
120
            neo.lib.logging.info('Got a new UUID : %s' % dump(self.uuid))
121
        self.accepted = True
122 123

    def getPrimaryConnection(self, connector_handler):
Grégory Wisniewski's avatar
Grégory Wisniewski committed
124 125 126 127
        """
        Primary lookup/connection process.
        Returns when the connection is made.
        """
128
        neo.lib.logging.info('connecting to a primary master node')
129 130
        em, nm = self.app.em, self.app.nm
        index = 0
Vincent Pelletier's avatar
Vincent Pelletier committed
131
        self.current = None
132 133
        conn = None
        # retry until identified to the primary
134
        while not self.accepted:
135
            if self.current is None:
136
                # conn closed
137 138
                conn = None
                # select a master
139
                master_list = nm.getMasterList()
140 141 142 143 144 145 146
                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
147 148
                conn = ClientConnection(em, self, self.current,
                    connector_handler())
149
            # still processing
150
            em.poll(1)
151
        node = nm.getByUUID(conn.getUUID())
152 153 154 155
        return (node, conn, self.uuid, self.num_partitions, self.num_replicas)