Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (27.7 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_network,
45
                                     update_build_progress)
46
from snf_django.utils.testing import mocked_quotaholder
47
from synnefo.logic.rapi import GanetiApiError
48

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

    
53
## Test Callbacks
54

    
55

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

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

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

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

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

    
89
    def test_old_msg(self, client):
90
        from time import sleep
91
        from datetime import datetime
92
        old_time = time()
93
        sleep(0.01)
94
        new_time = datetime.fromtimestamp(time())
95
        vm = mfactory.VirtualMachineFactory(backendtime=new_time)
96
        vm.operstate = 'STOPPED'
97
        vm.save()
98
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
99
                              event_time=split_time(old_time),
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.assertEquals(db_vm.operstate, "STOPPED")
105
        self.assertEquals(db_vm.backendtime, new_time)
106

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

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

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

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

    
149
    @patch("synnefo.logic.rapi_pool.GanetiRapiClient")
150
    def test_remove_error(self, rapi, client):
151
        vm = mfactory.VirtualMachineFactory()
152
        # Also create a NIC
153
        msg = self.create_msg(operation='OP_INSTANCE_REMOVE',
154
                              status="error",
155
                              instance=vm.backend_vm_id)
156
        rapi().GetInstance.return_value = {}
157
        update_db(client, msg)
158
        db_vm = VirtualMachine.objects.get(id=vm.id)
159
        self.assertFalse(db_vm.deleted)
160

    
161
        rapi().GetInstance.side_effect = GanetiApiError(msg="msg",
162
                                                        code=503)
163
        update_db(client, msg)
164
        db_vm = VirtualMachine.objects.get(id=vm.id)
165
        self.assertFalse(db_vm.deleted)
166

    
167
        rapi().GetInstance.side_effect = GanetiApiError(msg="msg",
168
                                                        code=404)
169
        with mocked_quotaholder():
170
            update_db(client, msg)
171
        db_vm = VirtualMachine.objects.get(id=vm.id)
172
        self.assertTrue(db_vm.deleted)
173

    
174
    def test_create(self, client):
175
        vm = mfactory.VirtualMachineFactory()
176
        msg = self.create_msg(operation='OP_INSTANCE_CREATE',
177
                              instance=vm.backend_vm_id)
178
        update_db(client, msg)
179
        client.basic_ack.assert_called_once()
180
        db_vm = VirtualMachine.objects.get(id=vm.id)
181
        self.assertEqual(db_vm.operstate, 'STARTED')
182

    
183
    def test_create_error(self, client):
184
        """Test that error create sets vm to ERROR state"""
185
        vm = mfactory.VirtualMachineFactory()
186
        msg = self.create_msg(operation='OP_INSTANCE_CREATE',
187
                              instance=vm.backend_vm_id,
188
                              status='error')
189
        update_db(client, msg)
190
        client.basic_ack.assert_called_once()
191
        db_vm = VirtualMachine.objects.get(id=vm.id)
192
        self.assertEqual(db_vm.operstate, 'ERROR')
193

    
194
    def test_remove_from_error(self, client):
195
        """Test that error removes delete error builds"""
196
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
197
        # Also create a NIC
198
        mfactory.NetworkInterfaceFactory(machine=vm)
199
        msg = self.create_msg(operation='OP_INSTANCE_REMOVE',
200
                              instance=vm.backend_vm_id)
201
        with mocked_quotaholder():
202
            update_db(client, msg)
203
        client.basic_ack.assert_called_once()
204
        db_vm = VirtualMachine.objects.get(id=vm.id)
205
        self.assertEqual(db_vm.operstate, 'DESTROYED')
206
        self.assertTrue(db_vm.deleted)
207
        # Check that nics are deleted
208
        self.assertFalse(db_vm.nics.all())
209

    
210
    def test_other_error(self, client):
211
        """Test that other error messages do no affect the VM"""
212
        vm = mfactory.VirtualMachineFactory()
213
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
214
                              instance=vm.backend_vm_id,
215
                              status='error')
216
        update_db(client, msg)
217
        client.basic_ack.assert_called_once()
218
        db_vm = VirtualMachine.objects.get(id=vm.id)
219
        self.assertEqual(db_vm.operstate, vm.operstate)
220
        self.assertEqual(db_vm.backendtime, vm.backendtime)
221

    
222

    
223
@patch('synnefo.lib.amqp.AMQPClient')
224
class UpdateNetTest(TestCase):
225
    def create_msg(self, **kwargs):
226
        """Create snf-ganeti-hook message"""
227
        msg = {'event_time': split_time(time())}
228
        msg['type'] = 'ganeti-op-status'
229
        msg['operation'] = 'OP_INSTANCE_SET_PARAMS'
230
        msg['status'] = 'success'
231
        msg['jobId'] = 1
232
        msg['logmsg'] = 'Dummy Log'
233
        for key, val in kwargs.items():
234
            msg[key] = val
235
        message = {'body': json.dumps(msg)}
236
        return message
237

    
238
    def test_missing_attribute(self, client):
239
        update_db(client, json.dumps({'body': {}}))
240
        client.basic_nack.assert_called_once()
241

    
242
    def test_unhandled_exception(self, client):
243
        update_db(client, {})
244
        client.basic_reject.assert_called_once()
245

    
246
    def test_wrong_type(self, client):
247
        msg = self.create_msg(type="WRONG_TYPE")
248
        update_db(client, msg)
249
        client.basic_ack.assert_called_once()
250

    
251
    def test_missing_instance(self, client):
252
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
253
                              instance='foo')
254
        update_db(client, msg)
255
        client.basic_nack.assert_called_once()
256

    
257
    def test_no_nics(self, client):
258
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
259
        mfactory.NetworkInterfaceFactory(machine=vm)
260
        mfactory.NetworkInterfaceFactory(machine=vm)
261
        mfactory.NetworkInterfaceFactory(machine=vm)
262
        self.assertEqual(len(vm.nics.all()), 3)
263
        msg = self.create_msg(nics=[],
264
                              instance=vm.backend_vm_id)
265
        update_db(client, msg)
266
        client.basic_ack.assert_called_once()
267
        db_vm = VirtualMachine.objects.get(id=vm.id)
268
        self.assertEqual(len(db_vm.nics.all()), 0)
269

    
270
    def test_empty_nic(self, client):
271
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
272
        for public in [True, False]:
273
            net = mfactory.NetworkFactory(public=public)
274
            msg = self.create_msg(nics=[{'network': net.backend_id}],
275
                                  instance=vm.backend_vm_id)
276
            update_db(client, msg)
277
            client.basic_ack.assert_called_once()
278
            db_vm = VirtualMachine.objects.get(id=vm.id)
279
            nics = db_vm.nics.all()
280
            self.assertEqual(len(nics), 1)
281
            self.assertEqual(nics[0].index, 0)
282
            self.assertEqual(nics[0].ipv4, '')
283
            self.assertEqual(nics[0].ipv6, '')
284
            self.assertEqual(nics[0].mac, '')
285
            if public:
286
                self.assertEqual(nics[0].firewall_profile,
287
                                 settings.DEFAULT_FIREWALL_PROFILE)
288
            else:
289
                self.assertEqual(nics[0].firewall_profile, '')
290

    
291
    def test_full_nic(self, client):
292
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
293
        net = mfactory.NetworkFactory(subnet='10.0.0.0/24')
294
        pool = net.get_pool()
295
        self.assertTrue(pool.is_available('10.0.0.22'))
296
        pool.save()
297
        msg = self.create_msg(nics=[{'network': net.backend_id,
298
                                     'ip': '10.0.0.22',
299
                                     'mac': 'aa:bb:cc:00:11:22'}],
300
                              instance=vm.backend_vm_id)
301
        update_db(client, msg)
302
        client.basic_ack.assert_called_once()
303
        db_vm = VirtualMachine.objects.get(id=vm.id)
304
        nics = db_vm.nics.all()
305
        self.assertEqual(len(nics), 1)
306
        self.assertEqual(nics[0].index, 0)
307
        self.assertEqual(nics[0].ipv4, '10.0.0.22')
308
        self.assertEqual(nics[0].ipv6, '')
309
        self.assertEqual(nics[0].mac, 'aa:bb:cc:00:11:22')
310
        pool = net.get_pool()
311
        self.assertFalse(pool.is_available('10.0.0.22'))
312
        pool.save()
313

    
314

    
315
@patch('synnefo.lib.amqp.AMQPClient')
316
class UpdateNetworkTest(TestCase):
317
    def create_msg(self, **kwargs):
318
        """Create snf-ganeti-eventd message"""
319
        msg = {'event_time': split_time(time())}
320
        msg['type'] = 'ganeti-network-status'
321
        msg['status'] = 'success'
322
        msg['jobId'] = 1
323
        msg['logmsg'] = 'Dummy Log'
324
        for key, val in kwargs.items():
325
            msg[key] = val
326
        message = {'body': json.dumps(msg)}
327
        return message
328

    
329
    def test_missing_attribute(self, client):
330
        update_network(client, json.dumps({'body': {}}))
331
        client.basic_nack.assert_called_once()
332

    
333
    def test_unhandled_exception(self, client):
334
        update_network(client, {})
335
        client.basic_reject.assert_called_once()
336

    
337
    def test_wrong_type(self, client):
338
        msg = self.create_msg(type="WRONG_TYPE")
339
        update_network(client, msg)
340
        client.basic_ack.assert_called_once()
341

    
342
    def test_missing_network(self, client):
343
        msg = self.create_msg(operation='OP_NETWORK_CREATE',
344
                              network='foo')
345
        update_network(client, msg)
346
        client.basic_nack.assert_called_once()
347

    
348
    def test_create(self, client):
349
        back_network = mfactory.BackendNetworkFactory(operstate='PENDING')
350
        net = back_network.network
351
        net.state = 'ACTIVE'
352
        net.save()
353
        back1 = back_network.backend
354

    
355
        back_network2 = mfactory.BackendNetworkFactory(operstate='PENDING',
356
                                                       network=net)
357
        back2 = back_network2.backend
358
        # Message from first backend network
359
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
360
                              network=net.backend_id,
361
                              cluster=back1.clustername)
362
        update_network(client, msg)
363
        client.basic_ack.assert_called_once()
364

    
365
        back_net = BackendNetwork.objects.get(id=back_network.id)
366
        self.assertEqual(back_net.operstate, 'ACTIVE')
367
        db_net = Network.objects.get(id=net.id)
368
        self.assertEqual(db_net.state, 'ACTIVE')
369
        # msg from second backend network
370
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
371
                              network=net.backend_id,
372
                              cluster=back2.clustername)
373
        update_network(client, msg)
374
        client.basic_ack.assert_called_once()
375

    
376
        db_net = Network.objects.get(id=net.id)
377
        self.assertEqual(db_net.state, 'ACTIVE')
378
        back_net = BackendNetwork.objects.get(id=back_network.id)
379
        self.assertEqual(back_net.operstate, 'ACTIVE')
380

    
381
    def test_create_offline_backend(self, client):
382
        """Test network creation when a backend is offline"""
383
        net = mfactory.NetworkFactory(state='ACTIVE')
384
        bn1 = mfactory.BackendNetworkFactory(network=net)
385
        bn2 = mfactory.BackendNetworkFactory(network=net,
386
                                             backend__offline=True)
387
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
388
                              network=net.backend_id,
389
                              cluster=bn1.backend.clustername)
390
        update_network(client, msg)
391
        client.basic_ack.assert_called_once()
392
        new_net = Network.objects.get(id=net.id)
393
        self.assertEqual(new_net.state, 'ACTIVE')
394

    
395
    def test_disconnect(self, client):
396
        bn1 = mfactory.BackendNetworkFactory(operstate='ACTIVE')
397
        net1 = bn1.network
398
        net1.state = "ACTIVE"
399
        net1.state = 'ACTIVE'
400
        net1.save()
401
        bn2 = mfactory.BackendNetworkFactory(operstate='ACTIVE',
402
                                             network=net1)
403
        msg = self.create_msg(operation='OP_NETWORK_DISCONNECT',
404
                              network=net1.backend_id,
405
                              cluster=bn2.backend.clustername)
406
        update_network(client, msg)
407
        client.basic_ack.assert_called_once()
408
        self.assertEqual(Network.objects.get(id=net1.id).state, 'ACTIVE')
409
        self.assertEqual(BackendNetwork.objects.get(id=bn2.id).operstate,
410
                         'PENDING')
411

    
412
    def test_remove(self, client):
413
        mfactory.MacPrefixPoolTableFactory()
414
        mfactory.BridgePoolTableFactory()
415
        bn = mfactory.BackendNetworkFactory(operstate='ACTIVE')
416
        for old_state in ['success', 'canceled', 'error']:
417
            for flavor in Network.FLAVORS.keys():
418
                bn.operstate = old_state
419
                bn.save()
420
                net = bn.network
421
                net.state = 'ACTIVE'
422
                net.flavor = flavor
423
                if flavor == 'PHYSICAL_VLAN':
424
                    net.link = allocate_resource('bridge')
425
                if flavor == 'MAC_FILTERED':
426
                    net.mac_prefix = allocate_resource('mac_prefix')
427
                net.save()
428
                msg = self.create_msg(operation='OP_NETWORK_REMOVE',
429
                                      network=net.backend_id,
430
                                      cluster=bn.backend.clustername)
431
                with mocked_quotaholder():
432
                    update_network(client, msg)
433
                client.basic_ack.assert_called_once()
434
                db_bnet = BackendNetwork.objects.get(id=bn.id)
435
                self.assertEqual(db_bnet.operstate,
436
                                 'DELETED')
437
                db_net = Network.objects.get(id=net.id)
438
                self.assertEqual(db_net.state, 'DELETED', flavor)
439
                self.assertTrue(db_net.deleted)
440
                if flavor == 'PHYSICAL_VLAN':
441
                    pool = BridgePoolTable.get_pool()
442
                    self.assertTrue(pool.is_available(net.link))
443
                if flavor == 'MAC_FILTERED':
444
                    pool = MacPrefixPoolTable.get_pool()
445
                    self.assertTrue(pool.is_available(net.mac_prefix))
446

    
447
    @patch("synnefo.logic.rapi_pool.GanetiRapiClient")
448
    def test_remove_error(self, rapi, client):
449
        mfactory.MacPrefixPoolTableFactory()
450
        mfactory.BridgePoolTableFactory()
451
        bn = mfactory.BackendNetworkFactory(operstate='ACTIVE')
452
        network = bn.network
453
        msg = self.create_msg(operation='OP_NETWORK_REMOVE',
454
                              network=network.backend_id,
455
                              status="error",
456
                              cluster=bn.backend.clustername)
457
        rapi().GetNetwork.return_value = {}
458
        update_network(client, msg)
459
        bn = BackendNetwork.objects.get(id=bn.id)
460
        self.assertNotEqual(bn.operstate, "DELETED")
461
        rapi().GetNetwork.side_effect = GanetiApiError(msg="foo", code=404)
462
        with mocked_quotaholder():
463
            update_network(client, msg)
464
        bn = BackendNetwork.objects.get(id=bn.id)
465
        self.assertEqual(bn.operstate, "DELETED")
466

    
467
    def test_remove_offline_backend(self, client):
468
        """Test network removing when a backend is offline"""
469
        mfactory.BridgePoolTableFactory()
470
        net = mfactory.NetworkFactory(flavor='PHYSICAL_VLAN',
471
                                      state='ACTIVE',
472
                                      link='prv12')
473
        bn1 = mfactory.BackendNetworkFactory(network=net)
474
        mfactory.BackendNetworkFactory(network=net,
475
                                       operstate="ACTIVE",
476
                                       backend__offline=True)
477
        msg = self.create_msg(operation='OP_NETWORK_REMOVE',
478
                              network=net.backend_id,
479
                              cluster=bn1.backend.clustername)
480
        with mocked_quotaholder():
481
            update_network(client, msg)
482
        client.basic_ack.assert_called_once()
483
        new_net = Network.objects.get(id=net.id)
484
        self.assertEqual(new_net.state, 'ACTIVE')
485
        self.assertFalse(new_net.deleted)
486

    
487
    def test_error_opcode(self, client):
488
        mfactory.MacPrefixPoolTableFactory()
489
        mfactory.BridgePoolTableFactory()
490
        for state, _ in Network.OPER_STATES:
491
            bn = mfactory.BackendNetworkFactory(operstate="ACTIVE")
492
            bn.operstate = state
493
            bn.save()
494
            network = bn.network
495
            network.state = state
496
            network.save()
497
            for opcode, _ in BackendNetwork.BACKEND_OPCODES:
498
                if opcode in ['OP_NETWORK_REMOVE', 'OP_NETWORK_ADD']:
499
                    continue
500
                msg = self.create_msg(operation=opcode,
501
                                      network=bn.network.backend_id,
502
                                      status='error',
503
                                      add_reserved_ips=[],
504
                                      remove_reserved_ips=[],
505
                                      cluster=bn.backend.clustername)
506
                with mocked_quotaholder():
507
                    update_network(client, msg)
508
                client.basic_ack.assert_called_once()
509
                db_bnet = BackendNetwork.objects.get(id=bn.id)
510
                self.assertEqual(bn.operstate, db_bnet.operstate)
511
                self.assertEqual(bn.network.state, db_bnet.network.state)
512

    
513
    def test_ips(self, client):
514
        network = mfactory.NetworkFactory(subnet='10.0.0.0/24')
515
        bn = mfactory.BackendNetworkFactory(network=network)
516
        msg = self.create_msg(operation='OP_NETWORK_SET_PARAMS',
517
                              network=network.backend_id,
518
                              cluster=bn.backend.clustername,
519
                              status='success',
520
                              add_reserved_ips=['10.0.0.10', '10.0.0.20'],
521
                              remove_reserved_ips=[])
522
        update_network(client, msg)
523
        client.basic_ack.assert_called_once()
524
        pool = network.get_pool()
525
        self.assertTrue(pool.is_reserved('10.0.0.10'))
526
        self.assertTrue(pool.is_reserved('10.0.0.20'))
527
        pool.save()
528
        # Release them
529
        msg = self.create_msg(operation='OP_NETWORK_SET_PARAMS',
530
                              network=network.backend_id,
531
                              cluster=bn.backend.clustername,
532
                              add_reserved_ips=[],
533
                              remove_reserved_ips=['10.0.0.10', '10.0.0.20'])
534
        update_network(client, msg)
535
        client.basic_ack.assert_called_once()
536
        pool = network.get_pool()
537
        self.assertFalse(pool.is_reserved('10.0.0.10'))
538
        self.assertFalse(pool.is_reserved('10.0.0.20'))
539

    
540

    
541
@patch('synnefo.lib.amqp.AMQPClient')
542
class UpdateBuildProgressTest(TestCase):
543
    def setUp(self):
544
        self.vm = mfactory.VirtualMachineFactory()
545

    
546
    def get_db_vm(self):
547
        return VirtualMachine.objects.get(id=self.vm.id)
548

    
549
    def create_msg(self, **kwargs):
550
        """Create snf-progress-monitor message"""
551
        msg = {'event_time': split_time(time())}
552
        msg['type'] = 'image-copy-progress'
553
        msg['progress'] = 0
554
        for key, val in kwargs.items():
555
            msg[key] = val
556
        message = {'body': json.dumps(msg)}
557
        return message
558

    
559
    def test_missing_attribute(self, client):
560
        update_build_progress(client, json.dumps({'body': {}}))
561
        client.basic_nack.assert_called_once()
562

    
563
    def test_unhandled_exception(self, client):
564
        update_build_progress(client, {})
565
        client.basic_reject.assert_called_once()
566

    
567
    def test_missing_instance(self, client):
568
        msg = self.create_msg(instance='foo')
569
        update_build_progress(client, msg)
570
        client.basic_nack.assert_called_once()
571

    
572
    def test_wrong_type(self, client):
573
        msg = self.create_msg(type="WRONG_TYPE")
574
        update_build_progress(client, msg)
575
        client.basic_ack.assert_called_once()
576

    
577
    def test_progress_update(self, client):
578
        rprogress = randint(10, 100)
579
        msg = self.create_msg(progress=rprogress,
580
                              instance=self.vm.backend_vm_id)
581
        update_build_progress(client, msg)
582
        client.basic_ack.assert_called_once()
583
        vm = self.get_db_vm()
584
        self.assertEqual(vm.buildpercentage, rprogress)
585

    
586
    def test_invalid_value(self, client):
587
        old = self.vm.buildpercentage
588
        for rprogress in [0, -1, 'a']:
589
            msg = self.create_msg(progress=rprogress,
590
                                  instance=self.vm.backend_vm_id)
591
            update_build_progress(client, msg)
592
            client.basic_ack.assert_called_once()
593
            vm = self.get_db_vm()
594
            self.assertEqual(vm.buildpercentage, old)
595

    
596

    
597
#class ReconciliationTest(TestCase):
598
#    SERVERS = 1000
599
#    fixtures = ['db_test_data']
600
#
601
#    def test_get_servers_from_db(self):
602
#        """Test getting a dictionary from each server to its operstate"""
603
#        self.assertEquals(reconciliation.get_servers_from_db(),
604
#                          {30000: 'STARTED', 30001: 'STOPPED', 30002: 'BUILD'})
605
#
606
#    def test_stale_servers_in_db(self):
607
#        """Test discovery of stale entries in DB"""
608
#
609
#        D = {1: 'STARTED', 2: 'STOPPED', 3: 'STARTED', 30000: 'BUILD',
610
#             30002: 'STOPPED'}
611
#        G = {1: True, 3: True, 30000: True}
612
#        self.assertEquals(reconciliation.stale_servers_in_db(D, G),
613
#                          set([2, 30002]))
614
#
615
#    @patch("synnefo.db.models.get_rapi_client")
616
#    def test_stale_building_vm(self, client):
617
#        vm = mfactory.VirtualMachineFactory()
618
#        vm.state = 'BUILD'
619
#        vm.backendjobid = 42
620
#        vm.save()
621
#        D = {vm.id: 'BUILD'}
622
#        G = {}
623
#        for status in ['queued', 'waiting', 'running']:
624
#            client.return_value.GetJobStatus.return_value = {'status': status}
625
#            self.assertEqual(reconciliation.stale_servers_in_db(D, G), set([]))
626
#            client.return_value.GetJobStatus\
627
#                               .assert_called_once_with(vm.backendjobid)
628
#            client.reset_mock()
629
#        for status in ['success', 'error', 'canceled']:
630
#            client.return_value.GetJobStatus.return_value = {'status': status}
631
#            self.assertEqual(reconciliation.stale_servers_in_db(D, G), set([]))
632
#            client.return_value.GetInstance\
633
#                               .assert_called_once_with(vm.backend_vm_id)
634
#            client.return_value.GetJobStatus\
635
#                               .assert_called_once_with(vm.backendjobid)
636
#            client.reset_mock()
637
#        from synnefo.logic.rapi import GanetiApiError
638
#        client.return_value.GetJobStatus.side_effect = GanetiApiError('Foo')
639
#        self.assertEqual(reconciliation.stale_servers_in_db(D, G),
640
#                         set([vm.id]))
641
#
642
#    def test_orphan_instances_in_ganeti(self):
643
#        """Test discovery of orphan instances in Ganeti, without a DB entry"""
644
#
645
#        G = {1: True, 2: False, 3: False, 4: True, 50: True}
646
#        D = {1: True, 3: False}
647
#        self.assertEquals(reconciliation.orphan_instances_in_ganeti(D, G),
648
#                          set([2, 4, 50]))
649
#
650
#    def test_unsynced_operstate(self):
651
#        """Test discovery of unsynced operstate between the DB and Ganeti"""
652
#
653
#        G = {1: True, 2: False, 3: True, 4: False, 50: True}
654
#        D = {1: 'STARTED', 2: 'STARTED', 3: 'BUILD', 4: 'STARTED', 50: 'BUILD'}
655
#        self.assertEquals(reconciliation.unsynced_operstate(D, G),
656
#                          set([(2, 'STARTED', False),
657
#                               (3, 'BUILD', True), (4, 'STARTED', False),
658
#                               (50, 'BUILD', True)]))
659

    
660
from synnefo.logic.test.rapi_pool_tests import *
661
from synnefo.logic.test.utils_tests import *