Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / tests.py @ 47eeffa9

History | View | Annotate | Download (25.5 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 backend
40
from synnefo.logic import reconciliation
41
from synnefo.logic.utils import get_rsapi_state
42
from synnefo.lib.utils import split_time
43
from datetime import datetime
44
from mock import patch
45
from synnefo.api.util import allocate_resource
46
from synnefo.logic.callbacks import update_db, update_net, update_network
47

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

    
52
## Test Callbacks
53

    
54

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

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

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

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

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

    
88
    def test_start(self, client):
89
        vm = mfactory.VirtualMachineFactory()
90
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
91
                              instance=vm.backend_vm_id)
92
        update_db(client, msg)
93
        client.basic_ack.assert_called_once()
94
        db_vm = VirtualMachine.objects.get(id=vm.id)
95
        self.assertEqual(db_vm.operstate, 'STARTED')
96

    
97
    def test_stop(self, client):
98
        vm = mfactory.VirtualMachineFactory()
99
        msg = self.create_msg(operation='OP_INSTANCE_SHUTDOWN',
100
                              instance=vm.backend_vm_id)
101
        update_db(client, msg)
102
        client.basic_ack.assert_called_once()
103
        db_vm = VirtualMachine.objects.get(id=vm.id)
104
        self.assertEqual(db_vm.operstate, 'STOPPED')
105

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

    
115
    def test_remove(self, client):
116
        vm = mfactory.VirtualMachineFactory()
117
        # Also create a NIC
118
        mfactory.NetworkInterfaceFactory(machine=vm)
119
        msg = self.create_msg(operation='OP_INSTANCE_REMOVE',
120
                              instance=vm.backend_vm_id)
121
        update_db(client, msg)
122
        client.basic_ack.assert_called_once()
123
        db_vm = VirtualMachine.objects.get(id=vm.id)
124
        self.assertEqual(db_vm.operstate, 'DESTROYED')
125
        self.assertTrue(db_vm.deleted)
126
        # Check that nics are deleted
127
        self.assertFalse(db_vm.nics.all())
128

    
129
    def test_create(self, client):
130
        vm = mfactory.VirtualMachineFactory()
131
        msg = self.create_msg(operation='OP_INSTANCE_CREATE',
132
                              instance=vm.backend_vm_id)
133
        update_db(client, msg)
134
        client.basic_ack.assert_called_once()
135
        db_vm = VirtualMachine.objects.get(id=vm.id)
136
        self.assertEqual(db_vm.operstate, 'STARTED')
137

    
138
    def test_create_error(self, client):
139
        """Test that error create sets vm to ERROR state"""
140
        vm = mfactory.VirtualMachineFactory()
141
        msg = self.create_msg(operation='OP_INSTANCE_CREATE',
142
                              instance=vm.backend_vm_id,
143
                              status='error')
144
        update_db(client, msg)
145
        client.basic_ack.assert_called_once()
146
        db_vm = VirtualMachine.objects.get(id=vm.id)
147
        self.assertEqual(db_vm.operstate, 'ERROR')
148

    
149
    def test_remove_from_error(self, client):
150
        """Test that error removes delete error builds"""
151
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
152
        # Also create a NIC
153
        mfactory.NetworkInterfaceFactory(machine=vm)
154
        msg = self.create_msg(operation='OP_INSTANCE_REMOVE',
155
                              instance=vm.backend_vm_id)
156
        update_db(client, msg)
157
        client.basic_ack.assert_called_once()
158
        db_vm = VirtualMachine.objects.get(id=vm.id)
159
        self.assertEqual(db_vm.operstate, 'DESTROYED')
160
        self.assertTrue(db_vm.deleted)
161
        # Check that nics are deleted
162
        self.assertFalse(db_vm.nics.all())
163

    
164
    def test_other_error(self, client):
165
        """Test that other error messages do no affect the VM"""
166
        vm = mfactory.VirtualMachineFactory()
167
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
168
                              instance=vm.backend_vm_id,
169
                              status='error')
170
        update_db(client, msg)
171
        client.basic_ack.assert_called_once()
172
        db_vm = VirtualMachine.objects.get(id=vm.id)
173
        self.assertEqual(db_vm.operstate, vm.operstate)
174
        self.assertEqual(db_vm.backendtime, vm.backendtime)
175

    
176

    
177
@patch('synnefo.lib.amqp.AMQPClient')
178
class UpdateNetTest(TestCase):
179
    def create_msg(self, **kwargs):
180
        """Create snf-ganeti-hook message"""
181
        msg = {'event_time': split_time(time())}
182
        msg['type'] = 'ganeti-net-status'
183
        msg['status'] = 'success'
184
        msg['jobId'] = 1
185
        msg['logmsg'] = 'Dummy Log'
186
        for key, val in kwargs.items():
187
            msg[key] = val
188
        message = {'body': json.dumps(msg)}
189
        return message
190

    
191
    def test_missing_attribute(self, client):
192
        update_net(client, json.dumps({'body': {}}))
193
        client.basic_nack.assert_called_once()
194

    
195
    def test_unhandled_exception(self, client):
196
        update_net(client, {})
197
        client.basic_reject.assert_called_once()
198

    
199
    def test_wrong_type(self, client):
200
        msg = self.create_msg(type="WRONG_TYPE")
201
        update_net(client, msg)
202
        client.basic_ack.assert_called_once()
203

    
204
    def test_missing_instance(self, client):
205
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
206
                              instance='foo')
207
        update_net(client, msg)
208
        client.basic_nack.assert_called_once()
209

    
210
    def test_no_nics(self, client):
211
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
212
        mfactory.NetworkInterfaceFactory(machine=vm)
213
        mfactory.NetworkInterfaceFactory(machine=vm)
214
        mfactory.NetworkInterfaceFactory(machine=vm)
215
        self.assertEqual(len(vm.nics.all()), 3)
216
        msg = self.create_msg(nics=[],
217
                              instance=vm.backend_vm_id)
218
        update_net(client, msg)
219
        client.basic_ack.assert_called_once()
220
        db_vm = VirtualMachine.objects.get(id=vm.id)
221
        self.assertEqual(len(db_vm.nics.all()), 0)
222

    
223
    def test_empty_nic(self, client):
224
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
225
        for public in [True, False]:
226
            net = mfactory.NetworkFactory(public=public)
227
            msg = self.create_msg(nics=[{'network':net.backend_id}],
228
                                  instance=vm.backend_vm_id)
229
            update_net(client, msg)
230
            client.basic_ack.assert_called_once()
231
            db_vm = VirtualMachine.objects.get(id=vm.id)
232
            nics = db_vm.nics.all()
233
            self.assertEqual(len(nics), 1)
234
            self.assertEqual(nics[0].index, 0)
235
            self.assertEqual(nics[0].ipv4, '')
236
            self.assertEqual(nics[0].ipv6, '')
237
            self.assertEqual(nics[0].mac, '')
238
            if public:
239
                self.assertEqual(nics[0].firewall_profile,
240
                                 settings.DEFAULT_FIREWALL_PROFILE)
241
            else:
242
                self.assertEqual(nics[0].firewall_profile, '')
243

    
244
    def test_full_nic(self, client):
245
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
246
        net = mfactory.NetworkFactory(subnet='10.0.0.0/24')
247
        pool = net.get_pool()
248
        self.assertTrue(pool.is_available('10.0.0.22'))
249
        pool.save()
250
        msg = self.create_msg(nics=[{'network':net.backend_id,
251
                                     'ip': '10.0.0.22',
252
                                     'mac': 'aa:bb:cc:00:11:22'}],
253
                              instance=vm.backend_vm_id)
254
        update_net(client, msg)
255
        client.basic_ack.assert_called_once()
256
        db_vm = VirtualMachine.objects.get(id=vm.id)
257
        nics = db_vm.nics.all()
258
        self.assertEqual(len(nics), 1)
259
        self.assertEqual(nics[0].index, 0)
260
        self.assertEqual(nics[0].ipv4, '10.0.0.22')
261
        self.assertEqual(nics[0].ipv6, '')
262
        self.assertEqual(nics[0].mac, 'aa:bb:cc:00:11:22')
263
        pool = net.get_pool()
264
        self.assertFalse(pool.is_available('10.0.0.22'))
265
        pool.save()
266

    
267

    
268
@patch('synnefo.lib.amqp.AMQPClient')
269
class UpdateNetworkTest(TestCase):
270
    def create_msg(self, **kwargs):
271
        """Create snf-ganeti-eventd message"""
272
        msg = {'event_time': split_time(time())}
273
        msg['type'] = 'ganeti-network-status'
274
        msg['status'] = 'success'
275
        msg['jobId'] = 1
276
        msg['logmsg'] = 'Dummy Log'
277
        for key, val in kwargs.items():
278
            msg[key] = val
279
        message = {'body': json.dumps(msg)}
280
        return message
281

    
282
    def test_missing_attribute(self, client):
283
        update_network(client, json.dumps({'body': {}}))
284
        client.basic_nack.assert_called_once()
285

    
286
    def test_unhandled_exception(self, client):
287
        update_network(client, {})
288
        client.basic_reject.assert_called_once()
289

    
290
    def test_wrong_type(self, client):
291
        msg = self.create_msg(type="WRONG_TYPE")
292
        update_network(client, msg)
293
        client.basic_ack.assert_called_once()
294

    
295
    def test_missing_network(self, client):
296
        msg = self.create_msg(operation='OP_NETWORK_CREATE',
297
                              network='foo')
298
        update_network(client, msg)
299
        client.basic_nack.assert_called_once()
300

    
301
    def test_create(self, client):
302
        back_network = mfactory.BackendNetworkFactory(operstate='PENDING')
303
        net = back_network.network
304
        back1 = back_network.backend
305

    
306
        back_network2 = mfactory.BackendNetworkFactory(operstate='PENDING',
307
                                                       network=net)
308
        back2 = back_network2.backend
309
        # Message from first backend network
310
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
311
                              network=net.backend_id,
312
                              cluster=back1.clustername)
313
        update_network(client, msg)
314
        client.basic_ack.assert_called_once()
315

    
316
        back_net = BackendNetwork.objects.get(id=back_network.id)
317
        self.assertEqual(back_net.operstate, 'ACTIVE')
318
        db_net = Network.objects.get(id=net.id)
319
        self.assertEqual(db_net.state, 'PENDING')
320
        # msg from second backend network
321
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
322
                              network=net.backend_id,
323
                              cluster=back2.clustername)
324
        update_network(client, msg)
325
        client.basic_ack.assert_called_once()
326

    
327
        db_net = Network.objects.get(id=net.id)
328
        self.assertEqual(db_net.state, 'ACTIVE')
329
        back_net = BackendNetwork.objects.get(id=back_network.id)
330
        self.assertEqual(back_net.operstate, 'ACTIVE')
331

    
332
    def test_disconnect(self, client):
333
        bn1 = mfactory.BackendNetworkFactory(operstate='ACTIVE')
334
        net1 = bn1.network
335
        net1.operstate = 'ACTIVE'
336
        net1.save()
337
        bn2 = mfactory.BackendNetworkFactory(operstate='ACTIVE',
338
                                             network=net1)
339
        msg = self.create_msg(operation='OP_NETWORK_DISCONNECT',
340
                              network=net1.backend_id,
341
                              cluster=bn2.backend.clustername)
342
        update_network(client, msg)
343
        client.basic_ack.assert_called_once()
344
        self.assertEqual(Network.objects.get(id=net1.id).state, 'PENDING')
345
        self.assertEqual(BackendNetwork.objects.get(id=bn2.id).operstate,
346
                        'PENDING')
347

    
348
    def test_remove(self, client):
349
        mfactory.MacPrefixPoolTableFactory()
350
        mfactory.BridgePoolTableFactory()
351
        bn = mfactory.BackendNetworkFactory(operstate='ACTIVE')
352
        for old_state in ['success', 'canceled', 'error']:
353
            for flavor in Network.FLAVORS.keys():
354
                bn.operstate = old_state
355
                bn.save()
356
                net = bn.network
357
                net.state = 'ACTIVE'
358
                net.flavor = flavor
359
                if flavor == 'PHYSICAL_VLAN':
360
                    net.link = allocate_resource('bridge')
361
                if flavor == 'MAC_FILTERED':
362
                    net.mac_prefix = allocate_resource('mac_prefix')
363
                net.save()
364
                msg = self.create_msg(operation='OP_NETWORK_REMOVE',
365
                                      network=net.backend_id,
366
                                      cluster=bn.backend.clustername)
367
                update_network(client, msg)
368
                client.basic_ack.assert_called_once()
369
                db_bnet = BackendNetwork.objects.get(id=bn.id)
370
                self.assertEqual(db_bnet.operstate,
371
                                'DELETED')
372
                db_net = Network.objects.get(id=net.id)
373
                self.assertEqual(db_net.state, 'DELETED', flavor)
374
                self.assertTrue(db_net.deleted)
375
                if flavor == 'PHYSICAL_VLAN':
376
                    pool = BridgePoolTable.get_pool()
377
                    self.assertTrue(pool.is_available(net.link))
378
                if flavor == 'MAC_FILTERED':
379
                    pool = MacPrefixPoolTable.get_pool()
380
                    self.assertTrue(pool.is_available(net.mac_prefix))
381

    
382
    def test_error_opcode(self, client):
383
        for state, _ in Network.OPER_STATES:
384
            bn = mfactory.BackendNetworkFactory()
385
            bn.operstate = state
386
            bn.save()
387
            network = bn.network
388
            network.state = state
389
            network.save()
390
            for opcode, _ in BackendNetwork.BACKEND_OPCODES:
391
                if opcode in ['OP_NETWORK_REMOVE', 'OP_NETWORK_ADD']:
392
                    continue
393
                msg = self.create_msg(operation=opcode,
394
                                      network=bn.network.backend_id,
395
                                      status='error',
396
                                      cluster=bn.backend.clustername)
397
                update_network(client, msg)
398
                client.basic_ack.assert_called_once()
399
                db_bnet = BackendNetwork.objects.get(id=bn.id)
400
                self.assertEqual(bn.operstate, db_bnet.operstate)
401
                self.assertEqual(bn.network.state, db_bnet.network.state)
402

    
403
    def test_ips(self, client):
404
        network = mfactory.NetworkFactory(subnet='10.0.0.0/24')
405
        bn = mfactory.BackendNetworkFactory(network=network)
406
        msg = self.create_msg(operation='OP_NETWORK_SET_PARAMS',
407
                              network=network.backend_id,
408
                              cluster=bn.backend.clustername,
409
                              status='success',
410
                              add_reserved_ips=['10.0.0.10', '10.0.0.20'],
411
                              remove_reserved_ips=[])
412
        update_network(client, msg)
413
        client.basic_ack.assert_called_once()
414
        pool = network.get_pool()
415
        self.assertTrue(pool.is_reserved('10.0.0.10'))
416
        self.assertTrue(pool.is_reserved('10.0.0.20'))
417
        pool.save()
418
        # Release them
419
        msg = self.create_msg(operation='OP_NETWORK_SET_PARAMS',
420
                              network=network.backend_id,
421
                              cluster=bn.backend.clustername,
422
                              add_reserved_ips=[],
423
                              remove_reserved_ips=['10.0.0.10', '10.0.0.20'])
424
        update_network(client, msg)
425
        client.basic_ack.assert_called_once()
426
        pool = network.get_pool()
427
        self.assertFalse(pool.is_reserved('10.0.0.10'))
428
        self.assertFalse(pool.is_reserved('10.0.0.20'))
429

    
430

    
431
class ProcessOpStatusTestCase(TestCase):
432
    fixtures = ['db_test_data']
433
    msg_op = {
434
        'instance': 'instance-name',
435
        'type': 'ganeti-op-status',
436
        'operation': 'OP_INSTANCE_STARTUP',
437
        'jobId': 0,
438
        'status': 'success',
439
        'logmsg': 'unittest - simulated message'
440
    }
441

    
442
    def test_op_startup_success(self):
443
        """Test notification for successful OP_INSTANCE_START"""
444
        msg = self.msg_op
445
        msg['operation'] = 'OP_INSTANCE_STARTUP'
446
        msg['status'] = 'success'
447

    
448
        # This machine is initially in BUILD
449
        vm = VirtualMachine.objects.get(pk=30002)
450
        backend.process_op_status(vm, now(), msg["jobId"], msg["operation"],
451
                                  msg["status"], msg["logmsg"])
452
        self.assertEquals(get_rsapi_state(vm), 'ACTIVE')
453

    
454
    def test_op_shutdown_success(self):
455
        """Test notification for successful OP_INSTANCE_SHUTDOWN"""
456
        msg = self.msg_op
457
        msg['operation'] = 'OP_INSTANCE_SHUTDOWN'
458
        msg['status'] = 'success'
459

    
460
        # This machine is initially in BUILD
461
        vm = VirtualMachine.objects.get(pk=30002)
462
        backend.process_op_status(vm, now(), msg["jobId"], msg["operation"],
463
                                  msg["status"], msg["logmsg"])
464
        self.assertEquals(get_rsapi_state(vm), 'STOPPED')
465

    
466
    def test_op_reboot_success(self):
467
        """Test notification for successful OP_INSTANCE_REBOOT"""
468
        msg = self.msg_op
469
        msg['operation'] = 'OP_INSTANCE_REBOOT'
470
        msg['status'] = 'success'
471

    
472
        # This machine is initially in BUILD
473
        vm = VirtualMachine.objects.get(pk=30002)
474
        backend.process_op_status(vm, now(), msg["jobId"], msg["operation"],
475
                                  msg["status"], msg["logmsg"])
476
        self.assertEquals(get_rsapi_state(vm), 'ACTIVE')
477

    
478
    def test_op_create_success(self):
479
        """Test notification for successful OP_INSTANCE_CREATE"""
480
        msg = self.msg_op
481
        msg['operation'] = 'OP_INSTANCE_CREATE'
482
        msg['status'] = 'success'
483

    
484
        # This machine is initially in BUILD
485
        vm = VirtualMachine.objects.get(pk=30002)
486
        backend.process_op_status(vm, now(), msg["jobId"], msg["operation"],
487
                                  msg["status"], msg["logmsg"])
488
        self.assertEquals(get_rsapi_state(vm), 'ACTIVE')
489

    
490
    def test_op_remove_success(self):
491
        """Test notification for successful OP_INSTANCE_REMOVE"""
492
        msg = self.msg_op
493
        msg['operation'] = 'OP_INSTANCE_REMOVE'
494
        msg['status'] = 'success'
495

    
496
        # This machine is initially in BUILD
497
        vm = VirtualMachine.objects.get(pk=30002)
498
        backend.process_op_status(vm, now(), msg["jobId"], msg["operation"],
499
                                  msg["status"], msg["logmsg"])
500
        self.assertEquals(get_rsapi_state(vm), 'DELETED')
501
        self.assertTrue(vm.deleted)
502

    
503
    def test_op_create_error(self):
504
        """Test notification for failed OP_INSTANCE_CREATE"""
505
        msg = self.msg_op
506
        msg['operation'] = 'OP_INSTANCE_CREATE'
507
        msg['status'] = 'error'
508

    
509
        # This machine is initially in BUILD
510
        vm = VirtualMachine.objects.get(pk=30002)
511
        backend.process_op_status(vm, now(), msg["jobId"], msg["operation"],
512
                                  msg["status"], msg["logmsg"])
513
        self.assertEquals(get_rsapi_state(vm), 'ERROR')
514
        self.assertFalse(vm.deleted)
515

    
516
    def test_remove_machine_in_error(self):
517
        """Test notification for failed OP_INSTANCE_REMOVE, server in ERROR"""
518
        msg = self.msg_op
519
        msg['operation'] = 'OP_INSTANCE_REMOVE'
520
        msg['status'] = 'error'
521

    
522
        # This machine is initially in BUILD
523
        vm = VirtualMachine.objects.get(pk=30002)
524
        backend.process_op_status(vm, now(), 0, "OP_INSTANCE_CREATE", "error", "test")
525
        self.assertEquals(get_rsapi_state(vm), 'ERROR')
526

    
527
        backend.process_op_status(vm, now(), msg["jobId"], msg["operation"],
528
                                  msg["status"], msg["logmsg"])
529
        self.assertEquals(get_rsapi_state(vm), 'DELETED')
530
        self.assertTrue(vm.deleted)
531

    
532

    
533
class ProcessNetStatusTestCase(TestCase):
534
    fixtures = ['db_test_data']
535

    
536
    def test_set_ipv4(self):
537
        """Test reception of a net status notification"""
538
        msg = {'instance': 'instance-name',
539
               'type':     'ganeti-net-status',
540
               'nics': [
541
                   {'ip': '10.0.0.21',
542
                    'mac': 'aa:00:00:58:1e:b9',
543
                    'network':'snf-net-30000'}
544
               ]
545
        }
546
        vm = VirtualMachine.objects.get(pk=30000)
547
        backend.process_net_status(vm, now(), msg['nics'])
548
        self.assertEquals(vm.nics.all()[0].ipv4, '10.0.0.21')
549

    
550
    def test_set_empty_ipv4(self):
551
        """Test reception of a net status notification with no IPv4 assigned"""
552
        msg = {'instance': 'instance-name',
553
               'type':     'ganeti-net-status',
554
               'nics': [
555
                   {'ip': '',
556
                    'mac': 'aa:00:00:58:1e:b9',
557
                    'network':'snf-net-30000'}
558
               ]
559
        }
560
        vm = VirtualMachine.objects.get(pk=30000)
561
        backend.process_net_status(vm, now(), msg['nics'])
562
        self.assertEquals(vm.nics.all()[0].ipv4, '')
563

    
564

    
565
class ProcessProgressUpdateTestCase(TestCase):
566
    fixtures = ['db_test_data']
567

    
568
    def test_progress_update(self):
569
        """Test reception of a create progress notification"""
570

    
571
        # This machine is in BUILD
572
        vm = VirtualMachine.objects.get(pk=30002)
573
        rprogress = randint(10, 100)
574

    
575
        backend.process_create_progress(vm, now(), rprogress)
576
        self.assertEquals(vm.buildpercentage, rprogress)
577

    
578
        #self.assertRaises(ValueError, backend.process_create_progress,
579
        #                  vm, 9, 0)
580
        self.assertRaises(ValueError, backend.process_create_progress,
581
                          now(), vm, -1)
582
        self.assertRaises(ValueError, backend.process_create_progress,
583
                          now(), vm, 'a')
584

    
585
        # This machine is ACTIVE
586
        #vm = VirtualMachine.objects.get(pk=30000)
587
        #self.assertRaises(VirtualMachine.IllegalState,
588
        #                  backend.process_create_progress, vm, 1)
589

    
590

    
591
class ReconciliationTestCase(TestCase):
592
    SERVERS = 1000
593
    fixtures = ['db_test_data']
594

    
595
    def test_get_servers_from_db(self):
596
        """Test getting a dictionary from each server to its operstate"""
597
        reconciliation.get_servers_from_db()
598
        self.assertEquals(reconciliation.get_servers_from_db(),
599
                          {30000: 'STARTED', 30001: 'STOPPED', 30002: 'BUILD'})
600

    
601
    def test_stale_servers_in_db(self):
602
        """Test discovery of stale entries in DB"""
603

    
604
        D = {1: 'STARTED', 2: 'STOPPED', 3: 'STARTED', 30000: 'BUILD',
605
             30002: 'STOPPED'}
606
        G = {1: True, 3: True, 30000: True}
607
        self.assertEquals(reconciliation.stale_servers_in_db(D, G),
608
                          set([2, 30002]))
609

    
610
    def test_orphan_instances_in_ganeti(self):
611
        """Test discovery of orphan instances in Ganeti, without a DB entry"""
612

    
613
        G = {1: True, 2: False, 3: False, 4: True, 50: True}
614
        D = {1: True, 3: False}
615
        self.assertEquals(reconciliation.orphan_instances_in_ganeti(D, G),
616
                          set([2, 4, 50]))
617

    
618
    def test_unsynced_operstate(self):
619
        """Test discovery of unsynced operstate between the DB and Ganeti"""
620

    
621
        G = {1: True, 2: False, 3: True, 4: False, 50: True}
622
        D = {1: 'STARTED', 2: 'STARTED', 3: 'BUILD', 4: 'STARTED', 50: 'BUILD'}
623
        self.assertEquals(reconciliation.unsynced_operstate(D, G),
624
                          set([(2, 'STARTED', False),
625
                           (3, 'BUILD', True), (4, 'STARTED', False),
626
                           (50, 'BUILD', True)]))