Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / tests / callbacks.py @ 6e9255ab

History | View | Annotate | Download (27.6 kB)

1
# vim: set fileencoding=utf-8 :
2
# Copyright 2013 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 (VirtualMachine, FloatingIP, BackendNetwork,
38
                               Network, BridgePoolTable, MacPrefixPoolTable)
39
from synnefo.db import models_factory as mfactory
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 import settings
48

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

    
53

    
54
## Test Callbacks
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
        self.assertTrue(client.basic_reject.called)
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
        self.assertTrue(client.basic_ack.called)
82

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

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

    
106
    def test_start(self, client):
107
        vm = mfactory.VirtualMachineFactory()
108
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
109
                              instance=vm.backend_vm_id)
110
        with mocked_quotaholder():
111
            update_db(client, msg)
112
        self.assertTrue(client.basic_ack.called)
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
        with mocked_quotaholder():
121
            update_db(client, msg)
122
        self.assertTrue(client.basic_ack.called)
123
        db_vm = VirtualMachine.objects.get(id=vm.id)
124
        self.assertEqual(db_vm.operstate, 'STOPPED')
125

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

    
135
    def test_remove(self, client):
136
        vm = mfactory.VirtualMachineFactory()
137
        # Also create a NIC
138
        nic = mfactory.NetworkInterfaceFactory(machine=vm)
139
        nic.network.get_pool().reserve(nic.ipv4)
140
        msg = self.create_msg(operation='OP_INSTANCE_REMOVE',
141
                              instance=vm.backend_vm_id)
142
        with mocked_quotaholder():
143
            update_db(client, msg)
144
        self.assertTrue(client.basic_ack.called)
145
        db_vm = VirtualMachine.objects.get(id=vm.id)
146
        self.assertEqual(db_vm.operstate, 'DESTROYED')
147
        self.assertTrue(db_vm.deleted)
148
        # Check that nics are deleted
149
        self.assertFalse(db_vm.nics.all())
150
        self.assertTrue(nic.network.get_pool().is_available(nic.ipv4))
151
        vm2 = mfactory.VirtualMachineFactory()
152
        network = mfactory.NetworkFactory(floating_ip_pool=True)
153
        fp1 = mfactory.FloatingIPFactory(machine=vm2, network=network)
154
        fp2 = mfactory.FloatingIPFactory(machine=vm2, network=network)
155
        mfactory.NetworkInterfaceFactory(machine=vm2, network=network,
156
                                         ipv4=fp1.ipv4)
157
        mfactory.NetworkInterfaceFactory(machine=vm2, network=network,
158
                                         ipv4=fp2.ipv4)
159
        pool = network.get_pool()
160
        pool.reserve(fp1.ipv4)
161
        pool.reserve(fp2.ipv4)
162
        pool.save()
163
        msg = self.create_msg(operation='OP_INSTANCE_REMOVE',
164
                              instance=vm2.backend_vm_id)
165
        with mocked_quotaholder():
166
            update_db(client, msg)
167
        client.basic_ack.assert_called_once()
168
        db_vm = VirtualMachine.objects.get(id=vm.id)
169
        self.assertEqual(db_vm.operstate, 'DESTROYED')
170
        self.assertTrue(db_vm.deleted)
171
        self.assertEqual(FloatingIP.objects.get(id=fp1.id).machine, None)
172
        self.assertEqual(FloatingIP.objects.get(id=fp2.id).machine, None)
173
        pool = network.get_pool()
174
        # Test that floating ips are not released
175
        self.assertFalse(pool.is_available(fp1.ipv4))
176
        self.assertFalse(pool.is_available(fp2.ipv4))
177

    
178
    def test_create(self, client):
179
        vm = mfactory.VirtualMachineFactory()
180
        msg = self.create_msg(operation='OP_INSTANCE_CREATE',
181
                              instance=vm.backend_vm_id)
182
        update_db(client, msg)
183
        self.assertTrue(client.basic_ack.called)
184
        db_vm = VirtualMachine.objects.get(id=vm.id)
185
        self.assertEqual(db_vm.operstate, 'STARTED')
186

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

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

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

    
226
    def test_resize_msg(self, client):
227
        vm = mfactory.VirtualMachineFactory()
228
        # Test empty beparams
229
        for status in ["success", "error"]:
230
            msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
231
                                  instance=vm.backend_vm_id,
232
                                  beparams={},
233
                                  status=status)
234
            client.reset_mock()
235
            with mocked_quotaholder():
236
                update_db(client, msg)
237
            self.assertTrue(client.basic_ack.called)
238
            db_vm = VirtualMachine.objects.get(id=vm.id)
239
            self.assertEqual(db_vm.operstate, vm.operstate)
240
        # Test intermediate states
241
        vm.operstate = "STOPPED"
242
        vm.save()
243
        for status in ["queued", "waiting", "running"]:
244
            msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
245
                                  instance=vm.backend_vm_id,
246
                                  beparams={"vcpus": 4, "minmem": 2048,
247
                                            "maxmem": 2048},
248
                                  status=status)
249
            client.reset_mock()
250
            update_db(client, msg)
251
            self.assertTrue(client.basic_ack.called)
252
            db_vm = VirtualMachine.objects.get(id=vm.id)
253
            self.assertEqual(db_vm.operstate, "STOPPED")
254
        # Test operstate after error
255
        msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
256
                              instance=vm.backend_vm_id,
257
                              beparams={"vcpus": 4},
258
                              status="error")
259
        client.reset_mock()
260
        with mocked_quotaholder():
261
            update_db(client, msg)
262
        self.assertTrue(client.basic_ack.called)
263
        db_vm = VirtualMachine.objects.get(id=vm.id)
264
        self.assertEqual(db_vm.operstate, "STOPPED")
265
        # Test success
266
        f1 = mfactory.FlavorFactory(cpu=4, ram=1024, disk_template="drbd",
267
                                    disk=1024)
268
        vm.flavor = f1
269
        vm.save()
270
        f2 = mfactory.FlavorFactory(cpu=8, ram=2048, disk_template="drbd",
271
                                    disk=1024)
272
        msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
273
                              instance=vm.backend_vm_id,
274
                              beparams={"vcpus": 8, "minmem": 2048,
275
                                        "maxmem": 2048},
276
                              status="success")
277
        client.reset_mock()
278
        with mocked_quotaholder():
279
            update_db(client, msg)
280
        self.assertTrue(client.basic_ack.called)
281
        db_vm = VirtualMachine.objects.get(id=vm.id)
282
        self.assertEqual(db_vm.operstate, "STOPPED")
283
        self.assertEqual(db_vm.flavor, f2)
284
        msg = self.create_msg(operation='OP_INSTANCE_SET_PARAMS',
285
                              instance=vm.backend_vm_id,
286
                              beparams={"vcpus": 100, "minmem": 2048,
287
                                        "maxmem": 2048},
288
                              status="success")
289
        client.reset_mock()
290
        with mocked_quotaholder():
291
            update_db(client, msg)
292
        self.assertTrue(client.basic_reject.called)
293

    
294

    
295
@patch('synnefo.lib.amqp.AMQPClient')
296
class UpdateNetTest(TestCase):
297
    def create_msg(self, **kwargs):
298
        """Create snf-ganeti-hook message"""
299
        msg = {'event_time': split_time(time())}
300
        msg['type'] = 'ganeti-op-status'
301
        msg['operation'] = 'OP_INSTANCE_SET_PARAMS'
302
        msg['status'] = 'success'
303
        msg['jobId'] = 1
304
        msg['logmsg'] = 'Dummy Log'
305
        for key, val in kwargs.items():
306
            msg[key] = val
307
        message = {'body': json.dumps(msg)}
308
        return message
309

    
310
    def test_missing_attribute(self, client):
311
        update_db(client, json.dumps({'body': {}}))
312
        self.assertTrue(client.basic_reject.called)
313

    
314
    def test_unhandled_exception(self, client):
315
        update_db(client, {})
316
        client.basic_reject.assert_called_once()
317

    
318
    def test_wrong_type(self, client):
319
        msg = self.create_msg(type="WRONG_TYPE")
320
        update_db(client, msg)
321
        self.assertTrue(client.basic_nack.called)
322

    
323
    def test_missing_instance(self, client):
324
        msg = self.create_msg(operation='OP_INSTANCE_STARTUP',
325
                              instance='foo')
326
        update_db(client, msg)
327
        self.assertTrue(client.basic_ack.called)
328

    
329
    def test_no_nics(self, client):
330
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
331
        mfactory.NetworkInterfaceFactory(machine=vm)
332
        mfactory.NetworkInterfaceFactory(machine=vm)
333
        mfactory.NetworkInterfaceFactory(machine=vm)
334
        self.assertEqual(len(vm.nics.all()), 3)
335
        msg = self.create_msg(nics=[],
336
                              instance=vm.backend_vm_id)
337
        update_db(client, msg)
338
        self.assertTrue(client.basic_ack.called)
339
        db_vm = VirtualMachine.objects.get(id=vm.id)
340
        self.assertEqual(len(db_vm.nics.all()), 0)
341

    
342
    def test_empty_nic(self, client):
343
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
344
        for public in [True, False]:
345
            net = mfactory.NetworkFactory(public=public, subnet6=None)
346
            msg = self.create_msg(nics=[{'network': net.backend_id}],
347
                                  instance=vm.backend_vm_id)
348
            update_db(client, msg)
349
            self.assertTrue(client.basic_ack.called)
350
            db_vm = VirtualMachine.objects.get(id=vm.id)
351
            nics = db_vm.nics.all()
352
            self.assertEqual(len(nics), 1)
353
            self.assertEqual(nics[0].index, 0)
354
            self.assertEqual(nics[0].ipv4, None)
355
            self.assertEqual(nics[0].ipv6, None)
356
            self.assertEqual(nics[0].mac, None)
357
            if public:
358
                self.assertEqual(nics[0].firewall_profile,
359
                                 settings.DEFAULT_FIREWALL_PROFILE)
360
            else:
361
                self.assertEqual(nics[0].firewall_profile, None)
362

    
363
    def test_full_nic(self, client):
364
        vm = mfactory.VirtualMachineFactory(operstate='ERROR')
365
        net = mfactory.NetworkFactory(subnet='10.0.0.0/24', subnet6=None)
366
        pool = net.get_pool()
367
        self.assertTrue(pool.is_available('10.0.0.22'))
368
        pool.save()
369
        msg = self.create_msg(nics=[{'network': net.backend_id,
370
                                     'ip': '10.0.0.22',
371
                                     'mac': 'aa:bb:cc:00:11:22'}],
372
                              instance=vm.backend_vm_id)
373
        update_db(client, msg)
374
        self.assertTrue(client.basic_ack.called)
375
        db_vm = VirtualMachine.objects.get(id=vm.id)
376
        nics = db_vm.nics.all()
377
        self.assertEqual(len(nics), 1)
378
        self.assertEqual(nics[0].index, 0)
379
        self.assertEqual(nics[0].ipv4, '10.0.0.22')
380
        self.assertEqual(nics[0].ipv6, None)
381
        self.assertEqual(nics[0].mac, 'aa:bb:cc:00:11:22')
382
        pool = net.get_pool()
383
        self.assertFalse(pool.is_available('10.0.0.22'))
384
        pool.save()
385

    
386

    
387
@patch('synnefo.lib.amqp.AMQPClient')
388
class UpdateNetworkTest(TestCase):
389
    def create_msg(self, **kwargs):
390
        """Create snf-ganeti-eventd message"""
391
        msg = {'event_time': split_time(time())}
392
        msg['type'] = 'ganeti-network-status'
393
        msg['status'] = 'success'
394
        msg['jobId'] = 1
395
        msg['logmsg'] = 'Dummy Log'
396
        for key, val in kwargs.items():
397
            msg[key] = val
398
        message = {'body': json.dumps(msg)}
399
        return message
400

    
401
    def test_missing_attribute(self, client):
402
        update_network(client, json.dumps({'body': {}}))
403
        self.assertTrue(client.basic_reject.called)
404

    
405
    def test_unhandled_exception(self, client):
406
        update_network(client, {})
407
        client.basic_reject.assert_called_once()
408

    
409
    def test_wrong_type(self, client):
410
        msg = self.create_msg(type="WRONG_TYPE")
411
        update_network(client, msg)
412
        self.assertTrue(client.basic_nack.called)
413

    
414
    def test_missing_network(self, client):
415
        msg = self.create_msg(operation='OP_NETWORK_CREATE',
416
                              network='foo')
417
        update_network(client, msg)
418
        self.assertTrue(client.basic_ack.called)
419

    
420
    def test_create(self, client):
421
        back_network = mfactory.BackendNetworkFactory(operstate='PENDING')
422
        net = back_network.network
423
        net.state = 'ACTIVE'
424
        net.save()
425
        back1 = back_network.backend
426

    
427
        back_network2 = mfactory.BackendNetworkFactory(operstate='PENDING',
428
                                                       network=net)
429
        back2 = back_network2.backend
430
        # Message from first backend network
431
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
432
                              network=net.backend_id,
433
                              cluster=back1.clustername)
434
        update_network(client, msg)
435
        self.assertTrue(client.basic_ack.called)
436

    
437
        back_net = BackendNetwork.objects.get(id=back_network.id)
438
        self.assertEqual(back_net.operstate, 'ACTIVE')
439
        db_net = Network.objects.get(id=net.id)
440
        self.assertEqual(db_net.state, 'ACTIVE')
441
        # msg from second backend network
442
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
443
                              network=net.backend_id,
444
                              cluster=back2.clustername)
445
        update_network(client, msg)
446
        self.assertTrue(client.basic_ack.called)
447

    
448
        db_net = Network.objects.get(id=net.id)
449
        self.assertEqual(db_net.state, 'ACTIVE')
450
        back_net = BackendNetwork.objects.get(id=back_network.id)
451
        self.assertEqual(back_net.operstate, 'ACTIVE')
452

    
453
    def test_create_offline_backend(self, client):
454
        """Test network creation when a backend is offline"""
455
        net = mfactory.NetworkFactory(state='ACTIVE')
456
        bn1 = mfactory.BackendNetworkFactory(network=net)
457
        mfactory.BackendNetworkFactory(network=net,
458
                                       backend__offline=True)
459
        msg = self.create_msg(operation='OP_NETWORK_CONNECT',
460
                              network=net.backend_id,
461
                              cluster=bn1.backend.clustername)
462
        update_network(client, msg)
463
        self.assertTrue(client.basic_ack.called)
464
        new_net = Network.objects.get(id=net.id)
465
        self.assertEqual(new_net.state, 'ACTIVE')
466

    
467
    def test_disconnect(self, client):
468
        bn1 = mfactory.BackendNetworkFactory(operstate='ACTIVE')
469
        net1 = bn1.network
470
        net1.state = "ACTIVE"
471
        net1.state = 'ACTIVE'
472
        net1.save()
473
        bn2 = mfactory.BackendNetworkFactory(operstate='ACTIVE',
474
                                             network=net1)
475
        msg = self.create_msg(operation='OP_NETWORK_DISCONNECT',
476
                              network=net1.backend_id,
477
                              cluster=bn2.backend.clustername)
478
        update_network(client, msg)
479
        self.assertTrue(client.basic_ack.called)
480
        self.assertEqual(Network.objects.get(id=net1.id).state, 'ACTIVE')
481
        self.assertEqual(BackendNetwork.objects.get(id=bn2.id).operstate,
482
                         'PENDING')
483

    
484
    def test_remove(self, client):
485
        mfactory.MacPrefixPoolTableFactory()
486
        mfactory.BridgePoolTableFactory()
487
        bn = mfactory.BackendNetworkFactory(operstate='ACTIVE')
488
        for old_state in ['success', 'canceled', 'error']:
489
            for flavor in Network.FLAVORS.keys():
490
                bn.operstate = old_state
491
                bn.save()
492
                net = bn.network
493
                net.state = 'ACTIVE'
494
                net.flavor = flavor
495
                if flavor == 'PHYSICAL_VLAN':
496
                    net.link = allocate_resource('bridge')
497
                if flavor == 'MAC_FILTERED':
498
                    net.mac_prefix = allocate_resource('mac_prefix')
499
                net.save()
500
                msg = self.create_msg(operation='OP_NETWORK_REMOVE',
501
                                      network=net.backend_id,
502
                                      cluster=bn.backend.clustername)
503
                with mocked_quotaholder():
504
                    update_network(client, msg)
505
                self.assertTrue(client.basic_ack.called)
506
                db_bnet = BackendNetwork.objects.get(id=bn.id)
507
                self.assertEqual(db_bnet.operstate,
508
                                 'DELETED')
509
                db_net = Network.objects.get(id=net.id)
510
                self.assertEqual(db_net.state, 'DELETED', flavor)
511
                self.assertTrue(db_net.deleted)
512
                if flavor == 'PHYSICAL_VLAN':
513
                    pool = BridgePoolTable.get_pool()
514
                    self.assertTrue(pool.is_available(net.link))
515
                if flavor == 'MAC_FILTERED':
516
                    pool = MacPrefixPoolTable.get_pool()
517
                    self.assertTrue(pool.is_available(net.mac_prefix))
518

    
519
    def test_remove_offline_backend(self, client):
520
        """Test network removing when a backend is offline"""
521
        mfactory.BridgePoolTableFactory()
522
        net = mfactory.NetworkFactory(flavor='PHYSICAL_VLAN',
523
                                      state='ACTIVE',
524
                                      link='prv12')
525
        bn1 = mfactory.BackendNetworkFactory(network=net)
526
        mfactory.BackendNetworkFactory(network=net,
527
                                       operstate="ACTIVE",
528
                                       backend__offline=True)
529
        msg = self.create_msg(operation='OP_NETWORK_REMOVE',
530
                              network=net.backend_id,
531
                              cluster=bn1.backend.clustername)
532
        with mocked_quotaholder():
533
            update_network(client, msg)
534
        self.assertTrue(client.basic_ack.called)
535
        new_net = Network.objects.get(id=net.id)
536
        self.assertEqual(new_net.state, 'ACTIVE')
537
        self.assertFalse(new_net.deleted)
538

    
539
    def test_error_opcode(self, client):
540
        mfactory.MacPrefixPoolTableFactory()
541
        mfactory.BridgePoolTableFactory()
542
        for state, _ in Network.OPER_STATES:
543
            bn = mfactory.BackendNetworkFactory(operstate="ACTIVE")
544
            bn.operstate = state
545
            bn.save()
546
            network = bn.network
547
            network.state = state
548
            network.save()
549
            for opcode, _ in BackendNetwork.BACKEND_OPCODES:
550
                if opcode in ['OP_NETWORK_REMOVE', 'OP_NETWORK_ADD']:
551
                    continue
552
                msg = self.create_msg(operation=opcode,
553
                                      network=bn.network.backend_id,
554
                                      status='error',
555
                                      add_reserved_ips=[],
556
                                      remove_reserved_ips=[],
557
                                      cluster=bn.backend.clustername)
558
                with mocked_quotaholder():
559
                    update_network(client, msg)
560
                self.assertTrue(client.basic_ack.called)
561
                db_bnet = BackendNetwork.objects.get(id=bn.id)
562
                self.assertEqual(bn.operstate, db_bnet.operstate)
563
                self.assertEqual(bn.network.state, db_bnet.network.state)
564

    
565
    def test_ips(self, client):
566
        network = mfactory.NetworkFactory(subnet='10.0.0.0/24')
567
        bn = mfactory.BackendNetworkFactory(network=network)
568
        msg = self.create_msg(operation='OP_NETWORK_SET_PARAMS',
569
                              network=network.backend_id,
570
                              cluster=bn.backend.clustername,
571
                              status='success',
572
                              add_reserved_ips=['10.0.0.10', '10.0.0.20'],
573
                              remove_reserved_ips=[])
574
        update_network(client, msg)
575
        self.assertTrue(client.basic_ack.called)
576
        pool = network.get_pool()
577
        self.assertTrue(pool.is_reserved('10.0.0.10'))
578
        self.assertTrue(pool.is_reserved('10.0.0.20'))
579
        pool.save()
580
        # Release them
581
        msg = self.create_msg(operation='OP_NETWORK_SET_PARAMS',
582
                              network=network.backend_id,
583
                              cluster=bn.backend.clustername,
584
                              add_reserved_ips=[],
585
                              remove_reserved_ips=['10.0.0.10', '10.0.0.20'])
586
        update_network(client, msg)
587
        self.assertTrue(client.basic_ack.called)
588
        pool = network.get_pool()
589
        self.assertFalse(pool.is_reserved('10.0.0.10'))
590
        self.assertFalse(pool.is_reserved('10.0.0.20'))
591

    
592

    
593
@patch('synnefo.lib.amqp.AMQPClient')
594
class UpdateBuildProgressTest(TestCase):
595
    def setUp(self):
596
        self.vm = mfactory.VirtualMachineFactory()
597

    
598
    def get_db_vm(self):
599
        return VirtualMachine.objects.get(id=self.vm.id)
600

    
601
    def create_msg(self, **kwargs):
602
        """Create snf-progress-monitor message"""
603
        msg = {'event_time': split_time(time())}
604
        msg['type'] = 'image-copy-progress'
605
        msg['progress'] = 0
606
        for key, val in kwargs.items():
607
            msg[key] = val
608
        message = {'body': json.dumps(msg)}
609
        return message
610

    
611
    def test_missing_attribute(self, client):
612
        update_build_progress(client, json.dumps({'body': {}}))
613
        self.assertTrue(client.basic_reject.called)
614

    
615
    def test_unhandled_exception(self, client):
616
        update_build_progress(client, {})
617
        client.basic_reject.assert_called_once()
618

    
619
    def test_missing_instance(self, client):
620
        msg = self.create_msg(instance='foo')
621
        update_build_progress(client, msg)
622
        self.assertTrue(client.basic_ack.called)
623

    
624
    def test_wrong_type(self, client):
625
        msg = self.create_msg(type="WRONG_TYPE")
626
        update_build_progress(client, msg)
627
        self.assertTrue(client.basic_nack.called)
628

    
629
    def test_progress_update(self, client):
630
        rprogress = randint(10, 100)
631
        msg = self.create_msg(progress=rprogress,
632
                              instance=self.vm.backend_vm_id)
633
        update_build_progress(client, msg)
634
        self.assertTrue(client.basic_ack.called)
635
        vm = self.get_db_vm()
636
        self.assertEqual(vm.buildpercentage, rprogress)
637

    
638
    def test_invalid_value(self, client):
639
        old = self.vm.buildpercentage
640
        for rprogress in [0, -1, 'a']:
641
            msg = self.create_msg(progress=rprogress,
642
                                  instance=self.vm.backend_vm_id)
643
            update_build_progress(client, msg)
644
            self.assertTrue(client.basic_ack.called)
645
            vm = self.get_db_vm()
646
            self.assertEqual(vm.buildpercentage, old)