testNodes.py 12 KB
Newer Older
1
#
Julien Muchembled's avatar
Julien Muchembled committed
2
# Copyright (C) 2009-2016  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 unittest
18
from mock import Mock
19 20
from neo.lib import protocol
from neo.lib.protocol import NodeTypes, NodeStates
21
from neo.lib.node import Node, NodeManager, MasterDB
22
from . import NeoUnitTestBase, getTempDirectory
23
from time import time
24 25
from os import chmod, mkdir, rmdir, unlink
from os.path import join, exists
26

27
class NodesTests(NeoUnitTestBase):
28 29

    def setUp(self):
30
        NeoUnitTestBase.setUp(self)
31
        self.nm = Mock()
32

33
    def _updatedByAddress(self, node, index=0):
34
        calls = self.nm.mockGetNamedCalls('_updateAddress')
35 36
        self.assertEqual(len(calls), index + 1)
        self.assertEqual(calls[index].getParam(0), node)
37

38
    def _updatedByUUID(self, node, index=0):
39
        calls = self.nm.mockGetNamedCalls('_updateUUID')
40 41 42 43 44
        self.assertEqual(len(calls), index + 1)
        self.assertEqual(calls[index].getParam(0), node)

    def testInit(self):
        """ Check the node initialization """
45
        address = ('127.0.0.1', 10000)
46
        uuid = self.getNewUUID(None)
47
        node = Node(self.nm, address=address, uuid=uuid)
48
        self.assertEqual(node.getState(), NodeStates.UNKNOWN)
49
        self.assertEqual(node.getAddress(), address)
50
        self.assertEqual(node.getUUID(), uuid)
51
        self.assertTrue(time() - 1 < node.getLastStateChange() < time())
52 53 54

    def testState(self):
        """ Check if the last changed time is updated when state is changed """
55
        node = Node(self.nm)
56
        self.assertEqual(node.getState(), NodeStates.UNKNOWN)
57 58
        self.assertTrue(time() - 1 < node.getLastStateChange() < time())
        previous_time = node.getLastStateChange()
59 60
        node.setState(NodeStates.RUNNING)
        self.assertEqual(node.getState(), NodeStates.RUNNING)
61 62 63
        self.assertTrue(previous_time < node.getLastStateChange())
        self.assertTrue(time() - 1 < node.getLastStateChange() < time())

64 65
    def testAddress(self):
        """ Check if the node is indexed by address """
66
        node = Node(self.nm)
67 68 69
        self.assertEqual(node.getAddress(), None)
        address = ('127.0.0.1', 10000)
        node.setAddress(address)
70
        self._updatedByAddress(node)
71 72

    def testUUID(self):
73
        """ As for Address but UUID """
74
        node = Node(self.nm)
75
        self.assertEqual(node.getAddress(), None)
76
        uuid = self.getNewUUID(None)
77 78 79 80
        node.setUUID(uuid)
        self._updatedByUUID(node)


81
class NodeManagerTests(NeoUnitTestBase):
82

83
    def _addStorage(self):
84 85
        self.storage = self.nm.createStorage(
            address=('127.0.0.1', 1000), uuid=self.getStorageUUID())
86 87

    def _addMaster(self):
88 89
        self.master = self.nm.createMaster(
            address=('127.0.0.1', 2000), uuid=self.getMasterUUID())
90 91

    def _addClient(self):
92
        self.client = self.nm.createClient(uuid=self.getClientUUID())
93 94

    def _addAdmin(self):
95 96
        self.admin = self.nm.createAdmin(
            address=('127.0.0.1', 4000), uuid=self.getAdminUUID())
97 98

    def checkNodes(self, node_list):
99
        self.assertEqual(sorted(self.nm.getList()), sorted(node_list))
100 101

    def checkMasters(self, master_list):
102
        self.assertEqual(self.nm.getMasterList(), master_list)
103 104

    def checkStorages(self, storage_list):
105
        self.assertEqual(self.nm.getStorageList(), storage_list)
106 107

    def checkClients(self, client_list):
108
        self.assertEqual(self.nm.getClientList(), client_list)
109 110

    def checkByServer(self, node):
111
        self.assertEqual(node, self.nm.getByAddress(node.getAddress()))
112

113
    def checkByUUID(self, node):
114
        self.assertEqual(node, self.nm.getByUUID(node.getUUID()))
115

116
    def checkIdentified(self, node_list, pool_set=None):
117
        identified_node_list = self.nm.getIdentifiedList(pool_set)
118 119
        self.assertEqual(set(identified_node_list), set(node_list))

120 121
    def testInit(self):
        """ Check the manager is empty when started """
122
        manager = self.nm
123 124 125 126
        self.checkNodes([])
        self.checkMasters([])
        self.checkStorages([])
        self.checkClients([])
127 128
        address = ('127.0.0.1', 10000)
        self.assertEqual(manager.getByAddress(address), None)
129
        self.assertEqual(manager.getByAddress(None), None)
130
        uuid = self.getNewUUID(None)
131 132
        self.assertEqual(manager.getByUUID(uuid), None)
        self.assertEqual(manager.getByUUID(None), None)
133 134 135

    def testAdd(self):
        """ Check if new nodes are registered in the manager """
136
        manager = self.nm
137 138
        self.checkNodes([])
        # storage
139
        self._addStorage()
140 141 142 143 144 145 146
        self.checkNodes([self.storage])
        self.checkStorages([self.storage])
        self.checkMasters([])
        self.checkClients([])
        self.checkByServer(self.storage)
        self.checkByUUID(self.storage)
        # master
147
        self._addMaster()
148 149 150 151 152 153 154
        self.checkNodes([self.storage, self.master])
        self.checkStorages([self.storage])
        self.checkMasters([self.master])
        self.checkClients([])
        self.checkByServer(self.master)
        self.checkByUUID(self.master)
        # client
155
        self._addClient()
156 157 158 159
        self.checkNodes([self.storage, self.master, self.client])
        self.checkStorages([self.storage])
        self.checkMasters([self.master])
        self.checkClients([self.client])
160 161
        # client node has no address
        self.assertEqual(manager.getByAddress(self.client.getAddress()), None)
162 163
        self.checkByUUID(self.client)
        # admin
164
        self._addAdmin()
165 166 167 168 169 170 171 172 173 174
        self.checkNodes([self.storage, self.master, self.client, self.admin])
        self.checkStorages([self.storage])
        self.checkMasters([self.master])
        self.checkClients([self.client])
        self.checkByServer(self.admin)
        self.checkByUUID(self.admin)

    def testUpdate(self):
        """ Check manager content update """
        # set up four nodes
175
        manager = self.nm
176 177 178 179
        self._addMaster()
        self._addStorage()
        self._addClient()
        self._addAdmin()
180 181 182 183 184
        self.checkNodes([self.master, self.storage, self.client, self.admin])
        self.checkMasters([self.master])
        self.checkStorages([self.storage])
        self.checkClients([self.client])
        # build changes
185
        old_address = self.master.getAddress()
186
        new_address = ('127.0.0.1', 2001)
187
        old_uuid = self.storage.getUUID()
188
        new_uuid = self.getStorageUUID()
189
        node_list = (
190 191 192 193 194 195
            (NodeTypes.CLIENT, None, self.client.getUUID(), NodeStates.DOWN),
            (NodeTypes.MASTER, new_address, self.master.getUUID(), NodeStates.RUNNING),
            (NodeTypes.STORAGE, self.storage.getAddress(), new_uuid,
                NodeStates.RUNNING),
            (NodeTypes.ADMIN, self.admin.getAddress(), self.admin.getUUID(),
                NodeStates.UNKNOWN),
196 197 198 199 200 201 202
        )
        # update manager content
        manager.update(node_list)
        # - the client gets down
        self.checkClients([])
        # - master change it's address
        self.checkMasters([self.master])
203
        self.assertEqual(manager.getByAddress(old_address), None)
204
        self.master.setAddress(new_address)
205
        self.checkByServer(self.master)
206
        # - storage change it's UUID
207 208 209 210
        storage_list = manager.getStorageList()
        self.assertTrue(len(storage_list), 1)
        new_storage = storage_list[0]
        self.assertNotEqual(new_storage.getUUID(), old_uuid)
211 212
        self.assertEqual(new_storage.getState(), NodeStates.RUNNING)
        # admin is still here but in UNKNOWN state
213
        self.checkNodes([self.master, self.admin, new_storage])
214
        self.assertEqual(self.admin.getState(), NodeStates.UNKNOWN)
215

216 217
    def testIdentified(self):
        # set up four nodes
218 219 220 221
        self._addMaster()
        self._addStorage()
        self._addClient()
        self._addAdmin()
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
        # switch node to connected
        self.checkIdentified([])
        self.master.setConnection(Mock())
        self.checkIdentified([self.master])
        self.storage.setConnection(Mock())
        self.checkIdentified([self.master, self.storage])
        self.client.setConnection(Mock())
        self.checkIdentified([self.master, self.storage, self.client])
        self.admin.setConnection(Mock())
        self.checkIdentified([self.master, self.storage, self.client, self.admin])
        # check the pool_set attribute
        self.checkIdentified([self.master], pool_set=[self.master.getUUID()])
        self.checkIdentified([self.storage], pool_set=[self.storage.getUUID()])
        self.checkIdentified([self.client], pool_set=[self.client.getUUID()])
        self.checkIdentified([self.admin], pool_set=[self.admin.getUUID()])
        self.checkIdentified([self.master, self.storage], pool_set=[
                self.master.getUUID(), self.storage.getUUID()])

240 241 242 243 244 245 246 247 248 249
class MasterDBTests(NeoUnitTestBase):
    def _checkMasterDB(self, path, expected_master_list):
        db = list(MasterDB(path))
        db_set = set(db)
        # Generic sanity check
        self.assertEqual(len(db), len(db_set))
        self.assertEqual(db_set, set(expected_master_list))

    def testInitialAccessRights(self):
        """
Julien Muchembled's avatar
Julien Muchembled committed
250
        Verify MasterDB raises immediately on instantiation if it cannot
251 252 253 254 255 256
        create a non-existing database. This does not guarantee any later
        open will succeed, but makes the simple error case obvious.
        """
        temp_dir = getTempDirectory()
        directory = join(temp_dir, 'read_only')
        db_file = join(directory, 'not_created')
257
        mkdir(directory, 0400)
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
        try:
            self.assertRaises(IOError, MasterDB, db_file)
        finally:
            rmdir(directory)

    def testLaterAccessRights(self):
        """
        Verify MasterDB does not raise when modifying database.
        """
        temp_dir = getTempDirectory()
        directory = join(temp_dir, 'read_write')
        db_file = join(directory, 'db')
        mkdir(directory)
        try:
            db = MasterDB(db_file)
            self.assertTrue(exists(db_file), db_file)
            chmod(db_file, 0400)
            address = ('example.com', 1024)
            # Must not raise
            db.add(address)
            # Value is stored
            self.assertTrue(address in db, [x for x in db])
            # But not visible to a new db instance (write access restored so
            # it can be created)
            chmod(db_file, 0600)
            db2 = MasterDB(db_file)
            self.assertFalse(address in db2, [x for x in db2])
        finally:
            if exists(db_file):
                unlink(db_file)
            rmdir(directory)

    def testPersistence(self):
        temp_dir = getTempDirectory()
        directory = join(temp_dir, 'read_write')
        db_file = join(directory, 'db')
        mkdir(directory)
        try:
            db = MasterDB(db_file)
            self.assertTrue(exists(db_file), db_file)
            address = ('example.com', 1024)
            db.add(address)
            address2 = ('example.org', 1024)
            db.add(address2)
            # Values are visible to a new db instance
            db2 = MasterDB(db_file)
            self.assertTrue(address in db2, [x for x in db2])
            self.assertTrue(address2 in db2, [x for x in db2])
            db.discard(address)
            # Create yet another instance (file is not supposed to be shared)
            db3 = MasterDB(db_file)
            self.assertFalse(address in db3, [x for x in db3])
            self.assertTrue(address2 in db3, [x for x in db3])
        finally:
            if exists(db_file):
                unlink(db_file)
            rmdir(directory)
315

316 317 318
if __name__ == '__main__':
    unittest.main()