Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / tests.py @ 8283d6c1

History | View | Annotate | Download (25 kB)

1
# vim: set fileencoding=utf-8 :
2
# Copyright 2012 GRNET S.A. All rights reserved.
3
#
4
# Redistribution and use in source and binary forms, with or without
5
# modification, are permitted provided that the following conditions
6
# are met:
7
#
8
#   1. Redistributions of source code must retain the above copyright
9
#      notice, this list of conditions and the following disclaimer.
10
#
11
#  2. Redistributions in binary form must reproduce the above copyright
12
#     notice, this list of conditions and the following disclaimer in the
13
#     documentation and/or other materials provided with the distribution.
14
#
15
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
16
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
19
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
# SUCH DAMAGE.
26
#
27
# The views and conclusions contained in the software and documentation are
28
# those of the authors and should not be interpreted as representing official
29
# policies, either expressed or implied, of GRNET S.A.
30

    
31
# Provides automated tests for logic module
32

    
33
from random import randint
34

    
35
from django.test import TestCase
36

    
37
from synnefo.db.models import *
38
from synnefo.db import models_factory as mfactory
39
from synnefo.logic import reconciliation
40
from synnefo.lib.utils import split_time
41
from datetime import datetime
42
from mock import patch
43
from synnefo.api.util import allocate_resource
44
from synnefo.logic.callbacks import (update_db, update_net, update_network,
45
                                     update_build_progress)
46

    
47
now = datetime.now
48
from time import time
49
import json
50

    
51
## Test Callbacks
52

    
53

    
54
@patch('synnefo.lib.amqp.AMQPClient')
55
class UpdateDBTest(TestCase):
56
    def create_msg(self, **kwargs):
57
        """Create snf-ganeti-eventd message"""
58
        msg = {'event_time': split_time(time())}
59
        msg['type'] = 'ganeti-op-status'
60
        msg['status'] = 'success'
61
        msg['jobId'] = 1
62
        msg['logmsg'] = 'Dummy Log'
63
        for key, val in kwargs.items():
64
            msg[key] = val
65
        message = {'body': json.dumps(msg)}
66
        return message
67

    
68
    def test_missing_attribute(self, client):
69
        update_db(client, json.dumps({'body': {}}))
70
        client.basic_nack.assert_called_once()
71

    
72
    def test_unhandled_exception(self, client):
73
        update_db(client, {})
74
        client.basic_reject.assert_called_once()
75

    
76
    def test_missing_instance(self, client):
77
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
78
                              instance='foo')
79
        update_db(client, msg)
80
        client.basic_nack.assert_called_once()
81

    
82
    def test_wrong_type(self, client):
83
        msg = self.create_msg(type="WRONG_TYPE")
84
        update_db(client, msg)
85
        client.basic_ack.assert_called_once()
86

    
87
    def test_old_msg(self, client):
88
        from time import sleep
89
        from datetime import datetime
90
        old_time = time()
91
        sleep(0.01)
92
        new_time = datetime.fromtimestamp(time())
93
        vm = mfactory.VirtualMachineFactory(backendtime=new_time)
94
        vm.operstate = 'STOPPED'
95
        vm.save()
96
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
97
                              event_time=split_time(old_time),
98
                              instance=vm.backend_vm_id)
99
        update_db(client, msg)
100
        client.basic_ack.assert_called_once()
101
        db_vm = VirtualMachine.objects.get(id=vm.id)
102
        self.assertEquals(db_vm.operstate, "STOPPED")
103
        self.assertEquals(db_vm.backendtime, new_time)
104

    
105
    def test_start(self, client):
106
        vm = mfactory.VirtualMachineFactory()
107
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
108
                              instance=vm.backend_vm_id)
109
        update_db(client, msg)
110
        client.basic_ack.assert_called_once()
111
        db_vm = VirtualMachine.objects.get(id=vm.id)
112
        self.assertEqual(db_vm.operstate, 'STARTED')
113

    
114
    def test_stop(self, client):
115
        vm = mfactory.VirtualMachineFactory()
116
        msg = self.create_msg(operation='OP_INSTANCE_SHUTDOWN',
117
                              instance=vm.backend_vm_id)
118
        update_db(client, msg)
119
        client.basic_ack.assert_called_once()
120
        db_vm = VirtualMachine.objects.get(id=vm.id)
121
        self.assertEqual(db_vm.operstate, 'STOPPED')
122

    
123
    def test_reboot(self, client):
124
        vm = mfactory.VirtualMachineFactory()
125
        msg = self.create_msg(operation='OP_INSTANCE_REBOOT',
126
                              instance=vm.backend_vm_id)
127
        update_db(client, msg)
128
        client.basic_ack.assert_called_once()
129
        db_vm = VirtualMachine.objects.get(id=vm.id)
130
        self.assertEqual(db_vm.operstate, 'STARTED')
131

    
132
    def test_remove(self, client):
133
        vm = mfactory.VirtualMachineFactory()
134
        # Also create a NIC
135
        mfactory.NetworkInterfaceFactory(machine=vm)
136
        msg = self.create_msg(operation='OP_INSTANCE_REMOVE',
137
                              instance=vm.backend_vm_id)
138
        update_db(client, msg)
139
        client.basic_ack.assert_called_once()
140
        db_vm = VirtualMachine.objects.get(id=vm.id)
141
        self.assertEqual(db_vm.operstate, 'DESTROYED')
142
        self.assertTrue(db_vm.deleted)
143
        # Check that nics are deleted
144
        self.assertFalse(db_vm.nics.all())
145

    
146
    def test_create(self, client):
147
        vm = mfactory.VirtualMachineFactory()
148
        msg = self.create_msg(operation='OP_INSTANCE_CREATE',
149
                              instance=vm.backend_vm_id)
150
        update_db(client, msg)
151
        client.basic_ack.assert_called_once()
152
        db_vm = VirtualMachine.objects.get(id=vm.id)
153
        self.assertEqual(db_vm.operstate, 'STARTED')
154

    
155
    def test_create_error(self, client):
156
        """Test that error create sets vm to ERROR state"""
157
        vm = mfactory.VirtualMachineFactory()
158
        msg = self.create_msg(operation='OP_INSTANCE_CREATE',
159
                              instance=vm.backend_vm_id,
160
                              status='error')
161
        update_db(client, msg)
162
        client.basic_ack.assert_called_once()
163
        db_vm = VirtualMachine.objects.get(id=vm.id)
164
        self.assertEqual(db_vm.operstate, 'ERROR')
165

    
166
    def test_remove_from_error(self, client):
167
        """Test that error removes delete error builds"""
168
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
169
        # Also create a NIC
170
        mfactory.NetworkInterfaceFactory(machine=vm)
171
        msg = self.create_msg(operation='OP_INSTANCE_REMOVE',
172
                              instance=vm.backend_vm_id)
173
        update_db(client, msg)
174
        client.basic_ack.assert_called_once()
175
        db_vm = VirtualMachine.objects.get(id=vm.id)
176
        self.assertEqual(db_vm.operstate, 'DESTROYED')
177
        self.assertTrue(db_vm.deleted)
178
        # Check that nics are deleted
179
        self.assertFalse(db_vm.nics.all())
180

    
181
    def test_other_error(self, client):
182
        """Test that other error messages do no affect the VM"""
183
        vm = mfactory.VirtualMachineFactory()
184
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
185
                              instance=vm.backend_vm_id,
186
                              status='error')
187
        update_db(client, msg)
188
        client.basic_ack.assert_called_once()
189
        db_vm = VirtualMachine.objects.get(id=vm.id)
190
        self.assertEqual(db_vm.operstate, vm.operstate)
191
        self.assertEqual(db_vm.backendtime, vm.backendtime)
192

    
193

    
194
@patch('synnefo.lib.amqp.AMQPClient')
195
class UpdateNetTest(TestCase):
196
    def create_msg(self, **kwargs):
197
        """Create snf-ganeti-hook message"""
198
        msg = {'event_time': split_time(time())}
199
        msg['type'] = 'ganeti-net-status'
200
        msg['status'] = 'success'
201
        msg['jobId'] = 1
202
        msg['logmsg'] = 'Dummy Log'
203
        for key, val in kwargs.items():
204
            msg[key] = val
205
        message = {'body': json.dumps(msg)}
206
        return message
207

    
208
    def test_missing_attribute(self, client):
209
        update_net(client, json.dumps({'body': {}}))
210
        client.basic_nack.assert_called_once()
211

    
212
    def test_unhandled_exception(self, client):
213
        update_net(client, {})
214
        client.basic_reject.assert_called_once()
215

    
216
    def test_wrong_type(self, client):
217
        msg = self.create_msg(type="WRONG_TYPE")
218
        update_net(client, msg)
219
        client.basic_ack.assert_called_once()
220

    
221
    def test_missing_instance(self, client):
222
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
223
                              instance='foo')
224
        update_net(client, msg)
225
        client.basic_nack.assert_called_once()
226

    
227
    def test_no_nics(self, client):
228
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
229
        mfactory.NetworkInterfaceFactory(machine=vm)
230
        mfactory.NetworkInterfaceFactory(machine=vm)
231
        mfactory.NetworkInterfaceFactory(machine=vm)
232
        self.assertEqual(len(vm.nics.all()), 3)
233
        msg = self.create_msg(nics=[],
234
                              instance=vm.backend_vm_id)
235
        update_net(client, msg)
236
        client.basic_ack.assert_called_once()
237
        db_vm = VirtualMachine.objects.get(id=vm.id)
238
        self.assertEqual(len(db_vm.nics.all()), 0)
239

    
240
    def test_empty_nic(self, client):
241
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
242
        for public in [True, False]:
243
            net = mfactory.NetworkFactory(public=public)
244
            msg = self.create_msg(nics=[{'network': net.backend_id}],
245
                                  instance=vm.backend_vm_id)
246
            update_net(client, msg)
247
            client.basic_ack.assert_called_once()
248
            db_vm = VirtualMachine.objects.get(id=vm.id)
249
            nics = db_vm.nics.all()
250
            self.assertEqual(len(nics), 1)
251
            self.assertEqual(nics[0].index, 0)
252
            self.assertEqual(nics[0].ipv4, '')
253
            self.assertEqual(nics[0].ipv6, '')
254
            self.assertEqual(nics[0].mac, '')
255
            if public:
256
                self.assertEqual(nics[0].firewall_profile,
257
                                 settings.DEFAULT_FIREWALL_PROFILE)
258
            else:
259
                self.assertEqual(nics[0].firewall_profile, '')
260

    
261
    def test_full_nic(self, client):
262
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
263
        net = mfactory.NetworkFactory(subnet='10.0.0.0/24')
264
        pool = net.get_pool()
265
        self.assertTrue(pool.is_available('10.0.0.22'))
266
        pool.save()
267
        msg = self.create_msg(nics=[{'network': net.backend_id,
268
                                     'ip': '10.0.0.22',
269
                                     'mac': 'aa:bb:cc:00:11:22'}],
270
                              instance=vm.backend_vm_id)
271
        update_net(client, msg)
272
        client.basic_ack.assert_called_once()
273
        db_vm = VirtualMachine.objects.get(id=vm.id)
274
        nics = db_vm.nics.all()
275
        self.assertEqual(len(nics), 1)
276
        self.assertEqual(nics[0].index, 0)
277
        self.assertEqual(nics[0].ipv4, '10.0.0.22')
278
        self.assertEqual(nics[0].ipv6, '')
279
        self.assertEqual(nics[0].mac, 'aa:bb:cc:00:11:22')
280
        pool = net.get_pool()
281
        self.assertFalse(pool.is_available('10.0.0.22'))
282
        pool.save()
283

    
284

    
285
@patch('synnefo.lib.amqp.AMQPClient')
286
class UpdateNetworkTest(TestCase):
287
    def create_msg(self, **kwargs):
288
        """Create snf-ganeti-eventd message"""
289
        msg = {'event_time': split_time(time())}
290
        msg['type'] = 'ganeti-network-status'
291
        msg['status'] = 'success'
292
        msg['jobId'] = 1
293
        msg['logmsg'] = 'Dummy Log'
294
        for key, val in kwargs.items():
295
            msg[key] = val
296
        message = {'body': json.dumps(msg)}
297
        return message
298

    
299
    def test_missing_attribute(self, client):
300
        update_network(client, json.dumps({'body': {}}))
301
        client.basic_nack.assert_called_once()
302

    
303
    def test_unhandled_exception(self, client):
304
        update_network(client, {})
305
        client.basic_reject.assert_called_once()
306

    
307
    def test_wrong_type(self, client):
308
        msg = self.create_msg(type="WRONG_TYPE")
309
        update_network(client, msg)
310
        client.basic_ack.assert_called_once()
311

    
312
    def test_missing_network(self, client):
313
        msg = self.create_msg(operation='OP_NETWORK_CREATE',
314
                              network='foo')
315
        update_network(client, msg)
316
        client.basic_nack.assert_called_once()
317

    
318
    def test_create(self, client):
319
        back_network = mfactory.BackendNetworkFactory(operstate='PENDING')
320
        net = back_network.network
321
        back1 = back_network.backend
322

    
323
        back_network2 = mfactory.BackendNetworkFactory(operstate='PENDING',
324
                                                       network=net)
325
        back2 = back_network2.backend
326
        # Message from first backend network
327
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
328
                              network=net.backend_id,
329
                              cluster=back1.clustername)
330
        update_network(client, msg)
331
        client.basic_ack.assert_called_once()
332

    
333
        back_net = BackendNetwork.objects.get(id=back_network.id)
334
        self.assertEqual(back_net.operstate, 'ACTIVE')
335
        db_net = Network.objects.get(id=net.id)
336
        self.assertEqual(db_net.state, 'PENDING')
337
        # msg from second backend network
338
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
339
                              network=net.backend_id,
340
                              cluster=back2.clustername)
341
        update_network(client, msg)
342
        client.basic_ack.assert_called_once()
343

    
344
        db_net = Network.objects.get(id=net.id)
345
        self.assertEqual(db_net.state, 'ACTIVE')
346
        back_net = BackendNetwork.objects.get(id=back_network.id)
347
        self.assertEqual(back_net.operstate, 'ACTIVE')
348

    
349
    def test_create_offline_backend(self, client):
350
        """Test network creation when a backend is offline"""
351
        net = mfactory.NetworkFactory()
352
        bn1 = mfactory.BackendNetworkFactory(network=net)
353
        bn2 = mfactory.BackendNetworkFactory(network=net,
354
                                             backend__offline=True)
355
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
356
                              network=net.backend_id,
357
                              cluster=bn1.backend.clustername)
358
        update_network(client, msg)
359
        client.basic_ack.assert_called_once()
360
        new_net = Network.objects.get(id=net.id)
361
        self.assertEqual(new_net.state, 'ACTIVE')
362

    
363
    def test_disconnect(self, client):
364
        bn1 = mfactory.BackendNetworkFactory(operstate='ACTIVE')
365
        net1 = bn1.network
366
        net1.operstate = 'ACTIVE'
367
        net1.save()
368
        bn2 = mfactory.BackendNetworkFactory(operstate='ACTIVE',
369
                                             network=net1)
370
        msg = self.create_msg(operation='OP_NETWORK_DISCONNECT',
371
                              network=net1.backend_id,
372
                              cluster=bn2.backend.clustername)
373
        update_network(client, msg)
374
        client.basic_ack.assert_called_once()
375
        self.assertEqual(Network.objects.get(id=net1.id).state, 'PENDING')
376
        self.assertEqual(BackendNetwork.objects.get(id=bn2.id).operstate,
377
                         'PENDING')
378

    
379
    def test_remove(self, client):
380
        mfactory.MacPrefixPoolTableFactory()
381
        mfactory.BridgePoolTableFactory()
382
        bn = mfactory.BackendNetworkFactory(operstate='ACTIVE')
383
        for old_state in ['success', 'canceled', 'error']:
384
            for flavor in Network.FLAVORS.keys():
385
                bn.operstate = old_state
386
                bn.save()
387
                net = bn.network
388
                net.state = 'ACTIVE'
389
                net.flavor = flavor
390
                if flavor == 'PHYSICAL_VLAN':
391
                    net.link = allocate_resource('bridge')
392
                if flavor == 'MAC_FILTERED':
393
                    net.mac_prefix = allocate_resource('mac_prefix')
394
                net.save()
395
                msg = self.create_msg(operation='OP_NETWORK_REMOVE',
396
                                      network=net.backend_id,
397
                                      cluster=bn.backend.clustername)
398
                update_network(client, msg)
399
                client.basic_ack.assert_called_once()
400
                db_bnet = BackendNetwork.objects.get(id=bn.id)
401
                self.assertEqual(db_bnet.operstate,
402
                                 'DELETED')
403
                db_net = Network.objects.get(id=net.id)
404
                self.assertEqual(db_net.state, 'DELETED', flavor)
405
                self.assertTrue(db_net.deleted)
406
                if flavor == 'PHYSICAL_VLAN':
407
                    pool = BridgePoolTable.get_pool()
408
                    self.assertTrue(pool.is_available(net.link))
409
                if flavor == 'MAC_FILTERED':
410
                    pool = MacPrefixPoolTable.get_pool()
411
                    self.assertTrue(pool.is_available(net.mac_prefix))
412

    
413
    def test_remove_offline_backend(self, client):
414
        """Test network removing when a backend is offline"""
415
        mfactory.BridgePoolTableFactory()
416
        net = mfactory.NetworkFactory(flavor='PHYSICAL_VLAN',
417
                                      state='ACTIVE',
418
                                      link='prv12')
419
        bn1 = mfactory.BackendNetworkFactory(network=net)
420
        mfactory.BackendNetworkFactory(network=net,
421
                                       backend__offline=True)
422
        msg = self.create_msg(operation='OP_NETWORK_REMOVE',
423
                              network=net.backend_id,
424
                              cluster=bn1.backend.clustername)
425
        update_network(client, msg)
426
        client.basic_ack.assert_called_once()
427
        new_net = Network.objects.get(id=net.id)
428
        self.assertEqual(new_net.state, 'DELETED')
429
        self.assertTrue(new_net.deleted)
430

    
431
    def test_error_opcode(self, client):
432
        for state, _ in Network.OPER_STATES:
433
            bn = mfactory.BackendNetworkFactory()
434
            bn.operstate = state
435
            bn.save()
436
            network = bn.network
437
            network.state = state
438
            network.save()
439
            for opcode, _ in BackendNetwork.BACKEND_OPCODES:
440
                if opcode in ['OP_NETWORK_REMOVE', 'OP_NETWORK_ADD']:
441
                    continue
442
                msg = self.create_msg(operation=opcode,
443
                                      network=bn.network.backend_id,
444
                                      status='error',
445
                                      cluster=bn.backend.clustername)
446
                update_network(client, msg)
447
                client.basic_ack.assert_called_once()
448
                db_bnet = BackendNetwork.objects.get(id=bn.id)
449
                self.assertEqual(bn.operstate, db_bnet.operstate)
450
                self.assertEqual(bn.network.state, db_bnet.network.state)
451

    
452
    def test_ips(self, client):
453
        network = mfactory.NetworkFactory(subnet='10.0.0.0/24')
454
        bn = mfactory.BackendNetworkFactory(network=network)
455
        msg = self.create_msg(operation='OP_NETWORK_SET_PARAMS',
456
                              network=network.backend_id,
457
                              cluster=bn.backend.clustername,
458
                              status='success',
459
                              add_reserved_ips=['10.0.0.10', '10.0.0.20'],
460
                              remove_reserved_ips=[])
461
        update_network(client, msg)
462
        client.basic_ack.assert_called_once()
463
        pool = network.get_pool()
464
        self.assertTrue(pool.is_reserved('10.0.0.10'))
465
        self.assertTrue(pool.is_reserved('10.0.0.20'))
466
        pool.save()
467
        # Release them
468
        msg = self.create_msg(operation='OP_NETWORK_SET_PARAMS',
469
                              network=network.backend_id,
470
                              cluster=bn.backend.clustername,
471
                              add_reserved_ips=[],
472
                              remove_reserved_ips=['10.0.0.10', '10.0.0.20'])
473
        update_network(client, msg)
474
        client.basic_ack.assert_called_once()
475
        pool = network.get_pool()
476
        self.assertFalse(pool.is_reserved('10.0.0.10'))
477
        self.assertFalse(pool.is_reserved('10.0.0.20'))
478

    
479

    
480
@patch('synnefo.lib.amqp.AMQPClient')
481
class UpdateBuildProgressTest(TestCase):
482
    def setUp(self):
483
        self.vm = mfactory.VirtualMachineFactory()
484

    
485
    def get_db_vm(self):
486
        return VirtualMachine.objects.get(id=self.vm.id)
487

    
488
    def create_msg(self, **kwargs):
489
        """Create snf-progress-monitor message"""
490
        msg = {'event_time': split_time(time())}
491
        msg['type'] = 'image-copy-progress'
492
        msg['progress'] = 0
493
        for key, val in kwargs.items():
494
            msg[key] = val
495
        message = {'body': json.dumps(msg)}
496
        return message
497

    
498
    def test_missing_attribute(self, client):
499
        update_build_progress(client, json.dumps({'body': {}}))
500
        client.basic_nack.assert_called_once()
501

    
502
    def test_unhandled_exception(self, client):
503
        update_build_progress(client, {})
504
        client.basic_reject.assert_called_once()
505

    
506
    def test_missing_instance(self, client):
507
        msg = self.create_msg(instance='foo')
508
        update_build_progress(client, msg)
509
        client.basic_nack.assert_called_once()
510

    
511
    def test_wrong_type(self, client):
512
        msg = self.create_msg(type="WRONG_TYPE")
513
        update_build_progress(client, msg)
514
        client.basic_ack.assert_called_once()
515

    
516
    def test_progress_update(self, client):
517
        rprogress = randint(10, 100)
518
        msg = self.create_msg(progress=rprogress,
519
                              instance=self.vm.backend_vm_id)
520
        update_build_progress(client, msg)
521
        client.basic_ack.assert_called_once()
522
        vm = self.get_db_vm()
523
        self.assertEqual(vm.buildpercentage, rprogress)
524

    
525
    def test_invalid_value(self, client):
526
        old = self.vm.buildpercentage
527
        for rprogress in [0, -1, 'a']:
528
            msg = self.create_msg(progress=rprogress,
529
                                  instance=self.vm.backend_vm_id)
530
            update_build_progress(client, msg)
531
            client.basic_ack.assert_called_once()
532
            vm = self.get_db_vm()
533
            self.assertEqual(vm.buildpercentage, old)
534

    
535

    
536
class ReconciliationTest(TestCase):
537
    SERVERS = 1000
538
    fixtures = ['db_test_data']
539

    
540
    def test_get_servers_from_db(self):
541
        """Test getting a dictionary from each server to its operstate"""
542
        self.assertEquals(reconciliation.get_servers_from_db(),
543
                          {30000: 'STARTED', 30001: 'STOPPED', 30002: 'BUILD'})
544

    
545
    def test_stale_servers_in_db(self):
546
        """Test discovery of stale entries in DB"""
547

    
548
        D = {1: 'STARTED', 2: 'STOPPED', 3: 'STARTED', 30000: 'BUILD',
549
             30002: 'STOPPED'}
550
        G = {1: True, 3: True, 30000: True}
551
        self.assertEquals(reconciliation.stale_servers_in_db(D, G),
552
                          set([2, 30002]))
553

    
554
    @patch("synnefo.db.models.get_rapi_client")
555
    def test_stale_building_vm(self, client):
556
        vm = mfactory.VirtualMachineFactory()
557
        vm.state = 'BUILD'
558
        vm.backendjobid = 42
559
        vm.save()
560
        D = {vm.id: 'BUILD'}
561
        G = {}
562
        for status in ['queued', 'waiting', 'running']:
563
            client.return_value.GetJobStatus.return_value = {'status': status}
564
            self.assertEqual(reconciliation.stale_servers_in_db(D, G), set([]))
565
            client.return_value.GetJobStatus\
566
                               .assert_called_once_with(vm.backendjobid)
567
            client.reset_mock()
568
        for status in ['success', 'error', 'canceled']:
569
            client.return_value.GetJobStatus.return_value = {'status': status}
570
            self.assertEqual(reconciliation.stale_servers_in_db(D, G), set([]))
571
            client.return_value.GetInstance\
572
                               .assert_called_once_with(vm.backend_vm_id)
573
            client.return_value.GetJobStatus\
574
                               .assert_called_once_with(vm.backendjobid)
575
            client.reset_mock()
576
        from synnefo.logic.rapi import GanetiApiError
577
        client.return_value.GetJobStatus.side_effect = GanetiApiError('Foo')
578
        self.assertEqual(reconciliation.stale_servers_in_db(D, G),
579
                         set([vm.id]))
580

    
581
    def test_orphan_instances_in_ganeti(self):
582
        """Test discovery of orphan instances in Ganeti, without a DB entry"""
583

    
584
        G = {1: True, 2: False, 3: False, 4: True, 50: True}
585
        D = {1: True, 3: False}
586
        self.assertEquals(reconciliation.orphan_instances_in_ganeti(D, G),
587
                          set([2, 4, 50]))
588

    
589
    def test_unsynced_operstate(self):
590
        """Test discovery of unsynced operstate between the DB and Ganeti"""
591

    
592
        G = {1: True, 2: False, 3: True, 4: False, 50: True}
593
        D = {1: 'STARTED', 2: 'STARTED', 3: 'BUILD', 4: 'STARTED', 50: 'BUILD'}
594
        self.assertEquals(reconciliation.unsynced_operstate(D, G),
595
                          set([(2, 'STARTED', False),
596
                               (3, 'BUILD', True), (4, 'STARTED', False),
597
                               (50, 'BUILD', True)]))
598

    
599
from synnefo.logic.test.rapi_pool_tests import *
600
from synnefo.logic.test.utils_tests import *