testMaster.py 7.81 KB
Newer Older
1
#
2 3 4 5 6 7 8 9 10 11 12
# Copyright (C) 2009  Nexedi SA
# 
# 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.
# 
# 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.
13
#
14 15 16 17
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

18
from time import sleep, time
19 20
import unittest
from neo.tests.functional import NEOCluster
21
from neo.neoctl.neoctl import NotReadyException
22
from neo import protocol
23
from neo.util import dump
24

Vincent Pelletier's avatar
Vincent Pelletier committed
25
DELAY_SAFETY_MARGIN = 5
26 27 28
MASTER_NODE_COUNT = 3

neo = NEOCluster([], port_base=20000, master_node_count=MASTER_NODE_COUNT)
Vincent Pelletier's avatar
Vincent Pelletier committed
29

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
class MasterTests(unittest.TestCase):

    def setUp(self):
        neo.stop()
        neo.start()
        self.storage = neo.getStorage()
        self.neoctl = neo.getNEOCTL()

    def tearDown(self):
        neo.stop()

    def _killMaster(self, primary=False, all=False):
        killed_uuid_list = []
        primary_uuid = self.neoctl.getPrimaryMaster()
        for master in neo.getMasterProcessList():
            master_uuid = master.getUUID()
            is_primary = master_uuid == primary_uuid
            if primary and is_primary or \
               not (primary or is_primary):
                 killed_uuid_list.append(master_uuid)
                 master.kill()
                 master.wait()
                 if not all:
                     break
        return killed_uuid_list

    def killPrimaryMaster(self):
        return self._killMaster(primary=True)

    def killSecondaryMaster(self, all=False):
Vincent Pelletier's avatar
Vincent Pelletier committed
60 61
        return self._killMaster(primary=False, all=all)

62 63 64 65 66
    def killMasters(self):
        secondary_list = self.killSecondaryMaster(all=True)
        primary_list = self.killPrimaryMaster()
        return secondary_list + primary_list

67 68 69
    def getMasterNodeList(self, state=None):
        return [x for x in self.neoctl.getNodeList(protocol.MASTER_NODE_TYPE)
                if state is None or x[3] == state]
70 71

    def getMasterNodeState(self, uuid):
Vincent Pelletier's avatar
Vincent Pelletier committed
72
        node_list = self.getMasterNodeList()
73 74 75 76 77 78 79
        for node_type, address, node_uuid, state in node_list:
            if node_uuid == uuid:
                break
        else:
            state = None
        return state

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
    def expectCondition(self, condition, timeout, delay):
        end = time() + timeout + DELAY_SAFETY_MARGIN
        opaque = None
        opaque_history = []
        while time() < end:
            reached, opaque = condition(opaque)
            if reached:
                break
            else:
                opaque_history.append(opaque)
                sleep(delay)
        else:
          raise AssertionError, 'Timeout while expecting condition. ' \
                                'History: %s' % (opaque_history, )

    def expectAllMasters(self, state=None, timeout=0, delay=1):
        def callback(last_try):
            current_try = len(self.getMasterNodeList(state=state))
            if last_try is not None and current_try < last_try:
                raise AssertionError, 'Regression: %s became %s' % \
                    (last_try, current_try)
            return (current_try == MASTER_NODE_COUNT, current_try)
        self.expectCondition(callback, timeout, delay)

    def expectMasterState(self, uuid, state, timeout=0, delay=1):
        if not isinstance(state, (tuple, list)):
            state = (state, )
        def callback(last_try):
            current_try = self.getMasterNodeState(uuid)
            return current_try in state, current_try
        self.expectCondition(callback, timeout, delay)

    def expectPrimaryMaster(self, uuid=None, timeout=0, delay=1):
        def callback(last_try):
            try:
                current_try = self.neoctl.getPrimaryMaster()
            except NotReadyException:
                current_try = None
            if None not in (uuid, current_try) and uuid != current_try:
                raise AssertionError, 'An unexpected primary arised: %r, ' \
                    'expected %r' % (dump(current_try), dump(uuid))
            return uuid is None or uuid == current_try, current_try
        self.expectCondition(callback, timeout, delay)

124
    def testStoppingSecondaryMaster(self):
125 126 127 128
        # Wait for masters to stabilize
        self.expectAllMasters()

        # Kill
129 130 131 132 133
        killed_uuid_list = self.killSecondaryMaster()
        # Test sanity check.
        self.assertEqual(len(killed_uuid_list), 1)
        uuid = killed_uuid_list[0]
        # Check node state has changed.
134
        self.expectMasterState(uuid, None)
135

Vincent Pelletier's avatar
Vincent Pelletier committed
136
    def testStoppingPrimaryMasterWithTwoSecondaries(self):
137 138 139 140
        # Wait for masters to stabilize
        self.expectAllMasters()

        # Kill
141 142 143 144
        killed_uuid_list = self.killPrimaryMaster()
        # Test sanity check.
        self.assertEqual(len(killed_uuid_list), 1)
        uuid = killed_uuid_list[0]
Vincent Pelletier's avatar
Vincent Pelletier committed
145
        # Check the state of the primary we just killed
146
        self.expectMasterState(uuid, (None, protocol.UNKNOWN_STATE))
147
        # Check that a primary master arised.
148
        self.expectPrimaryMaster(timeout=10)
149
        # Check that the uuid really changed.
150
        new_uuid = self.neoctl.getPrimaryMaster()
151 152
        self.assertNotEqual(new_uuid, uuid)

Vincent Pelletier's avatar
Vincent Pelletier committed
153
    def testStoppingPrimaryMasterWithOneSecondary(self):
154 155
        self.expectAllMasters(state=protocol.RUNNING_STATE)

Vincent Pelletier's avatar
Vincent Pelletier committed
156 157 158 159
        # Kill one secondary master.
        killed_uuid_list = self.killSecondaryMaster()
        # Test sanity checks.
        self.assertEqual(len(killed_uuid_list), 1)
160
        self.expectMasterState(killed_uuid_list[0], None)
Vincent Pelletier's avatar
Vincent Pelletier committed
161 162 163 164 165 166 167
        self.assertEqual(len(self.getMasterNodeList()), 2)

        killed_uuid_list = self.killPrimaryMaster()
        # Test sanity check.
        self.assertEqual(len(killed_uuid_list), 1)
        uuid = killed_uuid_list[0]
        # Check the state of the primary we just killed
168
        self.expectMasterState(uuid, (None, protocol.UNKNOWN_STATE))
Vincent Pelletier's avatar
Vincent Pelletier committed
169
        # Check that a primary master arised.
170
        self.expectPrimaryMaster(timeout=10)
Vincent Pelletier's avatar
Vincent Pelletier committed
171
        # Check that the uuid really changed.
172
        new_uuid = self.neoctl.getPrimaryMaster()
Vincent Pelletier's avatar
Vincent Pelletier committed
173 174 175
        self.assertNotEqual(new_uuid, uuid)

    def testMasterSequentialStart(self):
176
        self.expectAllMasters(state=protocol.RUNNING_STATE)
Vincent Pelletier's avatar
Vincent Pelletier committed
177
        master_list = neo.getMasterProcessList()
178 179 180

        # Stop the cluster (so we can start processes manually)
        self.killMasters()
Vincent Pelletier's avatar
Vincent Pelletier committed
181 182 183 184

        # Start the first master.
        first_master = master_list[0]
        first_master.start()
185
        first_master_uuid = first_master.getUUID()
Vincent Pelletier's avatar
Vincent Pelletier committed
186
        # Check that the master node we started elected itself.
187
        self.expectPrimaryMaster(first_master_uuid, timeout=60)
Vincent Pelletier's avatar
Vincent Pelletier committed
188 189 190 191 192

        # Start a second master.
        second_master = master_list[1]
        second_master.start()
        # Check that the second master is running under his known UUID.
193
        self.expectMasterState(second_master.getUUID(), protocol.RUNNING_STATE)
Vincent Pelletier's avatar
Vincent Pelletier committed
194
        # Check that the primary master didn't change.
195
        self.assertEqual(self.neoctl.getPrimaryMaster(), first_master_uuid)
Vincent Pelletier's avatar
Vincent Pelletier committed
196 197 198 199 200

        # Start a third master.
        third_master = master_list[2]
        third_master.start()
        # Check that the third master is running under his known UUID.
201
        self.expectMasterState(third_master.getUUID(), protocol.RUNNING_STATE)
Vincent Pelletier's avatar
Vincent Pelletier committed
202
        # Check that the primary master didn't change.
203
        self.assertEqual(self.neoctl.getPrimaryMaster(), first_master_uuid)
Vincent Pelletier's avatar
Vincent Pelletier committed
204

205 206 207 208 209 210
def test_suite():
    return unittest.makeSuite(MasterTests)

if __name__ == "__main__":
    unittest.main(defaultTest="test_suite")